Substituição sem distinção entre maiúsculas e minúsculas

173

Qual é a maneira mais fácil de fazer uma substituição de string que não diferencia maiúsculas de minúsculas no Python?

Adam Ernst
fonte

Respostas:

217

O stringtipo não suporta isso. Provavelmente é melhor usar o sub-método de expressão regular com a opção re.IGNORECASE .

>>> import re
>>> insensitive_hippo = re.compile(re.escape('hippo'), re.IGNORECASE)
>>> insensitive_hippo.sub('giraffe', 'I want a hIPpo for my birthday')
'I want a giraffe for my birthday'
Blair Conrad
fonte
11
Se você estiver apenas fazendo uma única substituição ou quiser salvar linhas de código, é mais eficiente usar uma única substituição com re.sub e o sinalizador (? I): re.sub ('(? I)' + re .escape ('hipopótamo'), 'girafa', 'quero um hipopótamo no meu aniversário')
D Coetzee
3
Por que procurar apenas uma sequência de letras? Obrigado.
Elena
8
@ Elena, não é necessário 'hippo', mas seria útil se o valor de substituição fosse passado para uma função, por isso é realmente mais um bom exemplo do que qualquer outra coisa.
Blair Conrad
2
Além de ter que re.escapeusar sua agulha, há outra armadilha aqui, que esta resposta falha em evitar, observada em stackoverflow.com/a/15831118/1709587 : como os re.subprocessos escapam das seqüências, conforme observado em docs.python.org/library/re.html#re .sub , você precisa escapar de todas as barras invertidas na cadeia de substituição ou usar uma lambda.
Mark Amery
84
import re
pattern = re.compile("hello", re.IGNORECASE)
pattern.sub("bye", "hello HeLLo HELLO")
# 'bye bye bye'
Desconhecido
fonte
17
Ou one-liner: re.sub('hello', 'bye', 'hello HeLLo HELLO', flags=re.IGNORECASE)
Louis Yang
Note que re.subapenas suporta este sinalizador desde o Python 2.7.
fuenfundachtzig 18/01/19
47

Em uma única linha:

import re
re.sub("(?i)hello","bye", "hello HeLLo HELLO") #'bye bye bye'
re.sub("(?i)he\.llo","bye", "he.llo He.LLo HE.LLO") #'bye bye bye'

Ou use o argumento opcional "flags":

import re
re.sub("hello", "bye", "hello HeLLo HELLO", flags=re.I) #'bye bye bye'
re.sub("he\.llo", "bye", "he.llo He.LLo HE.LLO", flags=re.I) #'bye bye bye'
viebel
fonte
14

Continuando na resposta do bFloch, essa função mudará não uma, mas todas as ocorrências antigas com as novas - de uma maneira que não faz distinção entre maiúsculas e minúsculas.

def ireplace(old, new, text):
    idx = 0
    while idx < len(text):
        index_l = text.lower().find(old.lower(), idx)
        if index_l == -1:
            return text
        text = text[:index_l] + new + text[index_l + len(old):]
        idx = index_l + len(new) 
    return text
rsmoorthy
fonte
Muito bem feito. Muito melhor que regex; ele lida com todos os tipos de caracteres, enquanto o regex é muito exigente com qualquer coisa não alfanumérica. Resposta preferida IMHO.
Fyngyrz
Tudo o que você precisa fazer é escapar do regex: a resposta aceita é muito mais curta e fácil de ler do que isso.
Físico louco
O Escape funciona apenas para correspondência, as barras invertidas no destino ainda podem atrapalhar as coisas.
usar o seguinte código
4

Como Blair Conrad diz que string.replace não suporta isso.

Use o regex re.sub, mas lembre-se de escapar primeiro da cadeia de substituição. Observe que não há opção de sinalizadores no 2.6 para re.sub, então você terá que usar o modificador incorporado'(?i)' (ou um objeto RE, consulte a resposta de Blair Conrad). Além disso, outra armadilha é que o sub processará escapes de barra invertida no texto de substituição, se uma string for fornecida. Para evitar isso, pode-se passar um lambda.

Aqui está uma função:

import re
def ireplace(old, repl, text):
    return re.sub('(?i)'+re.escape(old), lambda m: repl, text)

>>> ireplace('hippo?', 'giraffe!?', 'You want a hiPPO?')
'You want a giraffe!?'
>>> ireplace(r'[binfolder]', r'C:\Temp\bin', r'[BinFolder]\test.exe')
'C:\\Temp\\bin\\test.exe'
johv
fonte
4

Esta função usa as funções str.replace()e re.findall(). Ele substituirá todas as ocorrências de patternin stringpor repluma maneira que não diferencia maiúsculas de minúsculas.

def replace_all(pattern, repl, string) -> str:
   occurences = re.findall(pattern, string, re.IGNORECASE)
   for occurence in occurences:
       string = string.replace(occurence, repl)
       return string
Nico Bako
fonte
3

Isso não requer RegularExp

def ireplace(old, new, text):
    """ 
    Replace case insensitive
    Raises ValueError if string not found
    """
    index_l = text.lower().index(old.lower())
    return text[:index_l] + new + text[index_l + len(old):] 
bFloch
fonte
3
Bom, no entanto, isso não altera todas as ocorrências antigas com novas, mas apenas a primeira ocorrência.
rsmoorthy
5
É menos legível que a versão regex. Não há necessidade de reinventar a roda aqui.
Johannes Bittner
Seria interessante fazer uma comparação de desempenho entre esta e as versões aprovadas, pois pode ser mais rápido, o que é importante para alguns aplicativos. Ou pode ser mais lento porque funciona mais em Python interpretado.
D Coetzee
2

Uma observação interessante sobre detalhes e opções de sintaxe:

Python 3.7.2 (tags / v3.7.2: 9a3ffc0492, 23 de dezembro de 2018, 23:09:28) [MSC v.1916 de 64 bits (AMD64)] no win32

import re
old = "TREEROOT treeroot TREerOot"
re.sub(r'(?i)treeroot', 'grassroot', old)

«grassroot grassroot grassroot»

re.sub(r'treeroot', 'grassroot', old)

«TREEROOT base TREerOot»

re.sub(r'treeroot', 'grassroot', old, flags=re.I)

«grassroot grassroot grassroot»

re.sub(r'treeroot', 'grassroot', old, re.I)

«TREEROOT base TREerOot»

Portanto, o prefixo (? I) na expressão de correspondência ou a adição de "flags = re.I" como quarto argumento resultará em uma correspondência que não diferencia maiúsculas de minúsculas. MAS, usar apenas "re.I" como o quarto argumento não resulta em correspondência que não diferencia maiúsculas de minúsculas.

Para comparação,

re.findall(r'treeroot', old, re.I)

['TREEROOT', 'treeroot', 'TREerOot']

re.findall(r'treeroot', old)

['treeroot']

Murray
fonte
Isso não fornece uma resposta para a pergunta. por favor edite sua resposta para garantir que ele melhora a outras respostas já presentes nesta questão.
hongsy 20/01
1

Como estava sendo convertido para as seqüências de escape (role um pouco para baixo), observei que re.sub converte caracteres de escape com barra invertida para escapar de seqüências.

Para impedir que eu escrevi o seguinte:

Substitua maiúsculas e minúsculas.

import re
    def ireplace(findtxt, replacetxt, data):
        return replacetxt.join(  re.compile(findtxt, flags=re.I).split(data)  )

Além disso, se você desejar que ele substitua pelos caracteres de escape, como as outras respostas aqui que estão obtendo os caracteres de bashslash com significado especial convertidos em seqüências de escape, decodifique sua localização e ou substitua a string. No Python 3, pode ser necessário fazer algo como .decode ("unicode_escape") # python3

findtxt = findtxt.decode('string_escape') # python2
replacetxt = replacetxt.decode('string_escape') # python2
data = ireplace(findtxt, replacetxt, data)

Testado em Python 2.7.8

Espero que ajude.

Stan S.
fonte
0

nunca postou uma resposta antes e este tópico é realmente antigo, mas eu vim com outra solução e achei que poderia obter sua resposta. Não sou experiente na programação Python, portanto, se houver inconvenientes aparentes, indique-os desde o seu bom aprendizado: )

i='I want a hIPpo for my birthday'
key='hippo'
swp='giraffe'

o=(i.lower().split(key))
c=0
p=0
for w in o:
    o[c]=i[p:p+len(w)]
    p=p+len(key+w)
    c+=1
print(swp.join(o))
anddan
fonte
2
Para aprender: geralmente, quando você faz uma pesquisa e substitui uma string, é melhor não precisar transformá-la em uma matriz primeiro. É por isso que a primeira resposta é provavelmente a melhor. Enquanto estiver usando um módulo externo, está tratando a string como uma string inteira. Também é um pouco mais claro o que está acontecendo no processo.
Isaaclw
Para aprender: é muito difícil para um desenvolvedor sem contexto para ler este código e decifrar o que seu fazer :)
Todd