Caso insensible reemplazar

173

¿Cuál es la forma más fácil de hacer un reemplazo de cadena sin distinción entre mayúsculas y minúsculas en Python?

Adam Ernst
fuente

Respuestas:

217

El stringtipo no es compatible con esto. Probablemente sea mejor usar el submétodo de expresión regular con la opción 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
fuente
11
Si solo está haciendo un reemplazo simple, o desea guardar líneas de código, es más eficiente usar una sola sustitución con re.sub y el indicador (? I): re.sub ('(? I)' + re .escape ('hipopótamo'), 'jirafa', 'Quiero un hipopótamo para mi cumpleaños')
D Coetzee
3
¿Por qué escapar solo por una cadena de letras? Gracias.
Elena
8
@Elena, no es necesario 'hippo', pero sería útil si el valor de reemplazo se pasara a una función, por lo que es más un buen ejemplo que cualquier otra cosa.
Blair Conrad
2
Además de tener que re.escapeusar su aguja, aquí hay otra trampa que esta respuesta no puede evitar, anotada en stackoverflow.com/a/15831118/1709587 : dado que re.subprocesa secuencias de escape, como se indica en docs.python.org/library/re.html#re .sub , necesita escapar de todas las barras invertidas en su cadena de reemplazo o usar una lambda.
Mark Amery
84
import re
pattern = re.compile("hello", re.IGNORECASE)
pattern.sub("bye", "hello HeLLo HELLO")
# 'bye bye bye'
Desconocido
fuente
17
O una frase: re.sub('hello', 'bye', 'hello HeLLo HELLO', flags=re.IGNORECASE)
Louis Yang
Tenga en cuenta que re.subsolo admite esta bandera desde Python 2.7.
fuenfundachtzig
47

En una sola línea:

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'

O use el 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
fuente
14

Continuando con la respuesta de bFloch, esta función cambiará no una, sino todas las ocurrencias de lo antiguo con lo nuevo, en un caso insensible.

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
fuente
Muy bien hecho. Mucho mejor que regex; maneja todo tipo de caracteres, mientras que la expresión regular es muy exigente con todo lo que no sea alfanumérico. Respuesta preferida en mi humilde opinión.
fyngyrz
Todo lo que tiene que hacer es escapar de la expresión regular: la respuesta aceptada es mucho más corta y fácil de leer que esto.
Físico loco
Escape solo funciona para la coincidencia, las barras invertidas en el destino pueden arruinar las cosas aún.
ideasman42
4

Como Blair Conrad dice que string.replace no es compatible con esto.

Use la expresión regular re.sub, pero recuerde escapar primero de la cadena de reemplazo. Tenga en cuenta que no hay opción de banderas en 2.6 para re.sub, por lo que tendrá que usar el modificador incorporado '(?i)'(o un objeto RE, consulte la respuesta de Blair Conrad). Además, otro escollo es que sub procesará los escapes de barra invertida en el texto de reemplazo, si se proporciona una cadena. Para evitar esto, uno puede pasar una lambda.

Aquí hay una función:

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
fuente
4

Esta función usa las funciones str.replace()y re.findall(). Se reemplazará todas las apariciones de patternen stringla replforma de mayúsculas y 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
fuente
3

Esto no requiere 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
fuente
3
Buena, sin embargo, esto no cambia todas las ocurrencias de lo antiguo con lo nuevo, sino solo la primera.
rsmoorthy
55
Es menos legible que la versión regex. No es necesario reinventar la rueda aquí.
Johannes Bittner
Sería interesante hacer una comparación de rendimiento entre esta y las versiones votadas, podría ser más rápido, lo que es importante para algunas aplicaciones. O podría ser más lento porque hace más trabajo en Python interpretado.
D Coetzee
2

Una observación interesante sobre los detalles y opciones de sintaxis:

Python 3.7.2 (etiquetas / v3.7.2: 9a3ffc0492, 23 de diciembre de 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] en win32

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

'la base la base la base'

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

'TREEROOT TREerOot de base'

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

'la base la base la base'

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

'TREEROOT TREerOot de base'

Entonces, el prefijo (? I) en la expresión de coincidencia o agregar "flags = re.I" como cuarto argumento dará como resultado una coincidencia que no distingue entre mayúsculas y minúsculas. PERO, usar solo "re.I" como el cuarto argumento no da como resultado una coincidencia entre mayúsculas y minúsculas.

Para comparacion,

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

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

re.findall(r'treeroot', old)

['raíz del arbol']

Murray
fuente
Esto no proporciona una respuesta a la pregunta. Por favor, editar su respuesta para asegurarse de que mejora las otras respuestas que ya están presentes en esta pregunta.
hongsy
1

Estaba teniendo \ t siendo convertido a las secuencias de escape (desplazarse un poco hacia abajo), así que noté que re.sub convierte los caracteres escapados con barra invertida en secuencias de escape.

Para evitar eso escribí lo siguiente:

Reemplazar mayúsculas y minúsculas.

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

Además, si desea reemplazarlo con los caracteres de escape, como las otras respuestas aquí que obtienen los caracteres de bashslash de significado especial convertidos en secuencias de escape, simplemente decodifique su búsqueda y / o reemplace la cadena. En Python 3, podría tener que hacer algo como .decode ("unicode_escape") # python3

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

Probado en Python 2.7.8

Espero que ayude.

Stan S.
fuente
0

nunca publiqué una respuesta antes y este hilo es muy antiguo, pero se me ocurrió otra solución y pensé que podría obtener su respuesta, no estoy experimentado en la programación de Python, así que si hay inconvenientes aparentes, indíquelos ya que es un buen aprendizaje: )

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
fuente
2
Para aprender: generalmente cuando realiza una búsqueda y reemplazo en una cadena, es mejor no tener que convertirla primero en una matriz. Es por eso que la primera respuesta es probablemente la mejor. Mientras usa un módulo externo, trata la cadena como una cadena completa. También es un poco más claro lo que sucede en el proceso.
isaaclw
Para aprender: es muy difícil para un desarrollador sin contexto leer este código y descifrar lo que está haciendo :)
Todd