¿Cómo reemplazar múltiples subcadenas de una cadena?

284

Me gustaría usar la función .replace para reemplazar varias cadenas.

Actualmente tengo

string.replace("condition1", "")

pero me gustaría tener algo como

string.replace("condition1", "").replace("condition2", "text")

aunque eso no parece buena sintaxis

¿Cuál es la forma apropiada de hacer esto? algo así como cómo en grep / regex puedes hacer \1y \2reemplazar campos a ciertas cadenas de búsqueda

CQM
fuente
77
¿Probaste todas las soluciones proporcionadas? ¿Cuál es más rápido?
tommy.carstensen
Me he tomado el tiempo de probar todas las respuestas en diferentes escenarios. Ver stackoverflow.com/questions/59072514/…
Pablo
1
Honestamente, prefiero su enfoque encadenado a todos los demás. Aterricé aquí mientras buscaba una solución y usé la suya y funciona bien.
frakman1
@ frakman1 +1. No tengo idea de por qué esto no se vota más. Todos los demás métodos hacen que el código sea mucho más difícil de leer. Si hubiera una función de pasar matrices para reemplazar, esto funcionaría. Pero su método encadenado es más claro (al menos con un número estático de reemplazos)
IceFire

Respuestas:

269

Aquí hay un breve ejemplo que debería hacer el truco con expresiones regulares:

import re

rep = {"condition1": "", "condition2": "text"} # define desired replacements here

# use these three lines to do the replacement
rep = dict((re.escape(k), v) for k, v in rep.iteritems()) 
#Python 3 renamed dict.iteritems to dict.items so use rep.items() for latest versions
pattern = re.compile("|".join(rep.keys()))
text = pattern.sub(lambda m: rep[re.escape(m.group(0))], text)

Por ejemplo:

>>> pattern.sub(lambda m: rep[re.escape(m.group(0))], "(condition1) and --condition2--")
'() and --text--'
Andrew Clark
fuente
77
El reemplazo ocurre en una sola pasada.
Andrew Clark
26
dkamins: no es demasiado inteligente, ni siquiera es tan inteligente como debería ser (deberíamos regex-escape las teclas antes de unirlas con "|"). ¿Por qué no es eso ingeniería excesiva? porque de esta manera lo hacemos de una vez (= rápido), y hacemos todos los reemplazos al mismo tiempo, evitando enfrentamientos como "spamham sha".replace("spam", "eggs").replace("sha","md5")ser en "eggmd5m md5"lugar de"eggsham md5"
ovejas voladoras
8
@ AndrewClark Le agradecería mucho si pudiera explicar lo que está sucediendo en la última línea con lambda.
minerales
11
Hola, creé una pequeña esencia con una versión más clara de este fragmento. También debería ser un poco más eficiente: gist.github.com/bgusach/a967e0587d6e01e889fd1d776c5f3729
bgusach
15
Para python 3, use items () en lugar de iteritems ().
Jangari
127

Podrías hacer una pequeña y agradable función de bucle.

def replace_all(text, dic):
    for i, j in dic.iteritems():
        text = text.replace(i, j)
    return text

donde textestá la cadena completa y dices un diccionario: cada definición es una cadena que reemplazará una coincidencia con el término.

Nota : en Python 3, iteritems()se ha reemplazado conitems()


Cuidado: los diccionarios Python no tienen un orden confiable para la iteración. Esta solución solo resuelve su problema si:

  • orden de reemplazos es irrelevante
  • está bien que un reemplazo cambie los resultados de reemplazos anteriores

Por ejemplo:

d = { "cat": "dog", "dog": "pig"}
my_sentence = "This is my cat and this is my dog."
replace_all(my_sentence, d)
print(my_sentence)

Salida posible # 1:

"Este es mi cerdo y este es mi cerdo".

Salida posible # 2

"Este es mi perro y este es mi cerdo".

Una posible solución es usar un OrderedDict.

from collections import OrderedDict
def replace_all(text, dic):
    for i, j in dic.items():
        text = text.replace(i, j)
    return text
od = OrderedDict([("cat", "dog"), ("dog", "pig")])
my_sentence = "This is my cat and this is my dog."
replace_all(my_sentence, od)
print(my_sentence)

Salida:

"This is my pig and this is my pig."

Cuidado # 2: ineficiente si su textcadena es demasiado grande o si hay muchos pares en el diccionario.

Joseph Hansen
fuente
37
El orden en el que aplique los diferentes reemplazos será importante, por lo que, en lugar de usar un dict estándar, considere usar una OrderedDicto una lista de 2 tuplas.
slothrop
55
Esto hace que iterar la cadena dos veces ... no sea bueno para las actuaciones.
Valentin Lorentz
66
En cuanto al rendimiento, es peor que lo que dice Valentin: ¡recorrerá el texto tantas veces como haya elementos en dic! Está bien si 'texto' es pequeño pero terrible para texto grande.
JDonner
3
Esta es una buena solución para algunos casos. Por ejemplo, solo quiero sub 2 caracteres y no me importa el orden en que entran porque las teclas de sustitución no coinciden con ningún valor. Pero sí quiero que quede claro lo que está sucediendo.
Nathan Garabedian
55
Tenga en cuenta que esto puede dar resultados inesperados porque el texto recién insertado en la primera iteración puede coincidir en la segunda iteración. Por ejemplo, si intentamos ingenuamente reemplazar todo 'A' con 'B' y todo 'B' con 'C', la cadena 'AB' se transformaría en 'CC', y no en 'BC'.
Ambroz Bizjak
106

¿Por qué no una solución como esta?

s = "The quick brown fox jumps over the lazy dog"
for r in (("brown", "red"), ("lazy", "quick")):
    s = s.replace(*r)

#output will be:  The quick red fox jumps over the quick dog
Enrico Bianchi
fuente
2
Esto es súper útil, simple y portátil.
Triturar el
Se veía bien, pero no reemplaza la expresión regular como en: para r en ((r '\ s.', '.'), (R '\ s,', ',')):
Martin
2
para hacerlo 1 línea: ss = [s.replace (* r) para r en (("marrón", "rojo"), ("flojo", "rápido"))] [0]
Mark K
95

Aquí hay una variante de la primera solución que usa reducir, en caso de que le guste ser funcional. :)

repls = {'hello' : 'goodbye', 'world' : 'earth'}
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls.iteritems(), s)

La versión aún mejor de martineau:

repls = ('hello', 'goodbye'), ('world', 'earth')
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls, s)
Björn Lindqvist
fuente
8
Sería más simple hacer replsuna secuencia de tuplas y eliminar la iteritems()llamada. es decir repls = ('hello', 'goodbye'), ('world', 'earth')y reduce(lambda a, kv: a.replace(*kv), repls, s). También funcionaría sin cambios en Python 3.
martineau
¡bonito! si usa python3 use elementos en lugar de iteritems (ahora eliminados en material de dictado).
e.arbitrio
2
@martineau: No es cierto que esto funcione sin cambios en python3 ya que reducese ha eliminado .
normanius
55
@normanius: reducetodavía existe, sin embargo, se hizo parte del functoolsmódulo (ver los documentos ) en Python 3, así que cuando dije sin cambios, quise decir que se podía ejecutar el mismo código, aunque es cierto que requeriría que reducese haya importeditado si es necesario ya que ya no es incorporado.
martineau
35

Esta es solo una recapitulación más concisa de las excelentes respuestas de FJ y MiniQuark. Todo lo que necesita para lograr múltiples reemplazos de cadena simultáneos es la siguiente función:

def multiple_replace(string, rep_dict):
    pattern = re.compile("|".join([re.escape(k) for k in sorted(rep_dict,key=len,reverse=True)]), flags=re.DOTALL)
    return pattern.sub(lambda x: rep_dict[x.group(0)], string)

Uso:

>>>multiple_replace("Do you like cafe? No, I prefer tea.", {'cafe':'tea', 'tea':'cafe', 'like':'prefer'})
'Do you prefer tea? No, I prefer cafe.'

Si lo desea, puede realizar sus propias funciones de reemplazo dedicadas a partir de esta más simple.

mmj
fuente
1
Si bien esta es una buena solución, los reemplazos de cadenas concurrentes no darán exactamente los mismos resultados que si los realizara secuencialmente (encadenándolos), aunque eso puede no importar.
Martineau
2
Claro, con rep_dict = {"but": "mut", "mutton": "lamb"}la cadena "button"da como resultado "mutton"su código, pero daría "lamb"si los reemplazos estuvieran encadenados, uno tras otro.
Martineau
2
Esa es la característica principal de este código, no un defecto. Con reemplazos encadenados no podría lograr el comportamiento deseado de sustituir dos palabras simultáneamente y recíprocamente como en mi ejemplo.
mmj
1
No podría parecer una gran característica si no la necesita. Pero aquí estamos hablando de reemplazos simultáneos , entonces es de hecho la característica principal. Con reemplazos "encadenados", la salida del ejemplo sería Do you prefer cafe? No, I prefer cafe., lo cual no es deseable en absoluto.
mmj
@David escribe tu propia respuesta, tu edición es demasiado radical
UmNyobe
29

Construí esto sobre la excelente respuesta de FJ:

import re

def multiple_replacer(*key_values):
    replace_dict = dict(key_values)
    replacement_function = lambda match: replace_dict[match.group(0)]
    pattern = re.compile("|".join([re.escape(k) for k, v in key_values]), re.M)
    return lambda string: pattern.sub(replacement_function, string)

def multiple_replace(string, *key_values):
    return multiple_replacer(*key_values)(string)

Uso de un disparo:

>>> replacements = (u"café", u"tea"), (u"tea", u"café"), (u"like", u"love")
>>> print multiple_replace(u"Do you like café? No, I prefer tea.", *replacements)
Do you love tea? No, I prefer café.

Tenga en cuenta que dado que el reemplazo se realiza en una sola pasada, "café" cambia a "té", pero no vuelve a "café".

Si necesita hacer el mismo reemplazo muchas veces, puede crear una función de reemplazo fácilmente:

>>> my_escaper = multiple_replacer(('"','\\"'), ('\t', '\\t'))
>>> many_many_strings = (u'This text will be escaped by "my_escaper"',
                       u'Does this work?\tYes it does',
                       u'And can we span\nmultiple lines?\t"Yes\twe\tcan!"')
>>> for line in many_many_strings:
...     print my_escaper(line)
... 
This text will be escaped by \"my_escaper\"
Does this work?\tYes it does
And can we span
multiple lines?\t\"Yes\twe\tcan!\"

Mejoras:

  • convirtió el código en una función
  • soporte multilínea agregado
  • arreglado un error al escapar
  • fácil de crear una función para un reemplazo múltiple específico

¡Disfrutar! :-)

MiniQuark
fuente
1
¿Podría alguien explicar esto paso a paso para los novatos de Python como yo?
Julián Suárez
Compañero novato de Python aquí, así que voy a tomar una oportunidad incompleta para entenderlo ... a. desglosar key_values ​​en cosas para reemplazar (claves unidas por "|") y lógica (si la coincidencia es una clave, valor devuelto) b. crea un analizador de expresiones regulares ("patrón" que busca claves y utiliza la lógica dada); envuélvelo en una función lambda y devuélvelo. Cosas que estoy buscando ahora: re.M, y la necesidad de lambda para la lógica de reemplazo.
Fox
1
@ Fox Lo tienes. Podría definir una función en lugar de usar una lambda, es solo para acortar el código. Pero tenga en cuenta que pattern.subespera una función con solo un parámetro (el texto a reemplazar), por lo que la función debe tener acceso replace_dict. re.Mpermite reemplazos de líneas múltiples (se explica bien en el documento: docs.python.org/2/library/re.html#re.M ).
MiniQuark
22

Me gustaría proponer el uso de plantillas de cadena. ¡Simplemente coloque la cadena que se reemplazará en un diccionario y todo está listo! Ejemplo de docs.python.org

>>> from string import Template
>>> s = Template('$who likes $what')
>>> s.substitute(who='tim', what='kung pao')
'tim likes kung pao'
>>> d = dict(who='tim')
>>> Template('Give $who $100').substitute(d)
Traceback (most recent call last):
[...]
ValueError: Invalid placeholder in string: line 1, col 10
>>> Template('$who likes $what').substitute(d)
Traceback (most recent call last):
[...]
KeyError: 'what'
>>> Template('$who likes $what').safe_substitute(d)
'tim likes $what'
Fredrik Pihl
fuente
Se ve bien, pero al agregar una clave que no se proporciona, se substitutegenera una excepción, así que tenga cuidado al obtener plantillas de los usuarios.
Bart Friederichs el
2
Un inconveniente de este enfoque es que la plantilla debe contener todas, y no más que todas, las cadenas $ para ser reemplazadas, ver aquí
RolfBly
17

En mi caso, necesitaba un reemplazo simple de claves únicas con nombres, así que pensé esto:

a = 'This is a test string.'
b = {'i': 'I', 's': 'S'}
for x,y in b.items():
    a = a.replace(x, y)
>>> a
'ThIS IS a teSt StrIng.'
James Koss
fuente
3
Esto funciona siempre que no tenga un choque de reemplazo. Si lo reemplazara icon s, obtendría un comportamiento extraño.
bgusach
1
Si el orden es significativo, en lugar del dict anterior puede usar una matriz: b = [ ['i', 'Z'], ['s', 'Y'] ]; for x,y in (b): a = a.replace(x, y) luego, si tiene cuidado de ordenar sus pares de matriz, puede asegurarse de no reemplazar () de forma recursiva.
CÓDIGO-LEÍDO
Parece que los dictados ahora mantienen el orden , desde Python 3.7.0. Lo probé y funciona en orden en mi máquina con la última versión estable de Python 3.
James Koss
15

Comenzando Python 3.8, y la introducción de expresiones de asignación (PEP 572) ( :=operador), podemos aplicar los reemplazos dentro de una comprensión de lista:

# text = "The quick brown fox jumps over the lazy dog"
# replacements = [("brown", "red"), ("lazy", "quick")]
[text := text.replace(a, b) for a, b in replacements]
# text = 'The quick red fox jumps over the quick dog'
Xavier Guihot
fuente
¿Sabes si esto es más eficiente que usar replace in a loop? Estoy probando todas las respuestas para el rendimiento, pero todavía no tengo 3.8.
Pablo
¿Por qué obtengo el resultado en una lista?
johnrao07
1
@ johnrao07 Bueno, una comprensión de la lista construye una lista. Es por eso que, en este caso, obtienes ['The quick red fox jumps over the lazy dog', 'The quick red fox jumps over the quick dog']. Pero la expresión de asignación ( text := text.replace) también construye iterativamente nuevas versiones de textmutando. Después de la comprensión de la lista, puede usar la textvariable que contiene el texto modificado.
Xavier Guihot
1
Si desea devolver la nueva versión de textone-liner, también puede usar [text := text.replace(a, b) for a, b in replacements][-1](observe la [-1]), que extrae el último elemento de la comprensión de la lista; es decir, la última versión de text.
Xavier Guihot
13

Aquí mis $ 0.02. Se basa en la respuesta de Andrew Clark, un poco más clara, y también cubre el caso cuando una cadena para reemplazar es una subcadena de otra cadena para reemplazar (la cadena más larga gana)

def multireplace(string, replacements):
    """
    Given a string and a replacement map, it returns the replaced string.

    :param str string: string to execute replacements on
    :param dict replacements: replacement dictionary {value to find: value to replace}
    :rtype: str

    """
    # Place longer ones first to keep shorter substrings from matching
    # where the longer ones should take place
    # For instance given the replacements {'ab': 'AB', 'abc': 'ABC'} against 
    # the string 'hey abc', it should produce 'hey ABC' and not 'hey ABc'
    substrs = sorted(replacements, key=len, reverse=True)

    # Create a big OR regex that matches any of the substrings to replace
    regexp = re.compile('|'.join(map(re.escape, substrs)))

    # For each match, look up the new string in the replacements
    return regexp.sub(lambda match: replacements[match.group(0)], string)

Es en esto en lo esencial , siéntase libre de modificarlo si tiene alguna propuesta.

bgusach
fuente
1
Esta debería haber sido la respuesta aceptada en su lugar porque la expresión regular se construye a partir de todas las claves clasificándolas en orden descendente de longitud y uniéndolas con el | operador de alternancia regex. Y la clasificación es necesaria para que se seleccione la opción más larga posible si hay alguna alternativa.
Sachin S
Estoy de acuerdo en que esta es la mejor solución, gracias a la clasificación. Además de ordenar, es idéntico a mi respuesta original, así que tomé prestada la clasificación para mi solución también, para asegurarme de que nadie se pierda una característica tan importante.
mmj
6

Necesitaba una solución donde las cadenas a ser reemplazadas pueden ser expresiones regulares, por ejemplo, para ayudar a normalizar un texto largo reemplazando múltiples caracteres de espacios en blanco por uno solo. Partiendo de una cadena de respuestas de otros, incluidos MiniQuark y mmj, esto es lo que se me ocurrió:

def multiple_replace(string, reps, re_flags = 0):
    """ Transforms string, replacing keys from re_str_dict with values.
    reps: dictionary, or list of key-value pairs (to enforce ordering;
          earlier items have higher priority).
          Keys are used as regular expressions.
    re_flags: interpretation of regular expressions, such as re.DOTALL
    """
    if isinstance(reps, dict):
        reps = reps.items()
    pattern = re.compile("|".join("(?P<_%d>%s)" % (i, re_str[0])
                                  for i, re_str in enumerate(reps)),
                         re_flags)
    return pattern.sub(lambda x: reps[int(x.lastgroup[1:])][1], string)

Funciona para los ejemplos dados en otras respuestas, por ejemplo:

>>> multiple_replace("(condition1) and --condition2--",
...                  {"condition1": "", "condition2": "text"})
'() and --text--'

>>> multiple_replace('hello, world', {'hello' : 'goodbye', 'world' : 'earth'})
'goodbye, earth'

>>> multiple_replace("Do you like cafe? No, I prefer tea.",
...                  {'cafe': 'tea', 'tea': 'cafe', 'like': 'prefer'})
'Do you prefer tea? No, I prefer cafe.'

Lo principal para mí es que también puedes usar expresiones regulares, por ejemplo para reemplazar solo palabras enteras o para normalizar espacios en blanco:

>>> s = "I don't want to change this name:\n  Philip II of Spain"
>>> re_str_dict = {r'\bI\b': 'You', r'[\n\t ]+': ' '}
>>> multiple_replace(s, re_str_dict)
"You don't want to change this name: Philip II of Spain"

Si desea utilizar las teclas del diccionario como cadenas normales, puede escapar de ellas antes de llamar a multiple_replace utilizando, por ejemplo, esta función:

def escape_keys(d):
    """ transform dictionary d by applying re.escape to the keys """
    return dict((re.escape(k), v) for k, v in d.items())

>>> multiple_replace(s, escape_keys(re_str_dict))
"I don't want to change this name:\n  Philip II of Spain"

La siguiente función puede ayudar a encontrar expresiones regulares erróneas entre las teclas del diccionario (ya que el mensaje de error de multiple_replace no es muy revelador):

def check_re_list(re_list):
    """ Checks if each regular expression in list is well-formed. """
    for i, e in enumerate(re_list):
        try:
            re.compile(e)
        except (TypeError, re.error):
            print("Invalid regular expression string "
                  "at position {}: '{}'".format(i, e))

>>> check_re_list(re_str_dict.keys())

Tenga en cuenta que no encadena los reemplazos, sino que los realiza simultáneamente. Esto lo hace más eficiente sin limitar lo que puede hacer. Para imitar el efecto del encadenamiento, es posible que solo necesite agregar más pares de reemplazo de cadenas y garantizar el orden esperado de los pares:

>>> multiple_replace("button", {"but": "mut", "mutton": "lamb"})
'mutton'
>>> multiple_replace("button", [("button", "lamb"),
...                             ("but", "mut"), ("mutton", "lamb")])
'lamb'

fuente
Esto es bueno, gracias. ¿Podría mejorarse para permitir también que se utilicen referencias inversas en las sustituciones? No he descubierto de inmediato cómo agregar eso.
cmarqu
La respuesta a mi pregunta anterior es stackoverflow.com/questions/45630940/…
cmarqu
4

Aquí hay una muestra que es más eficiente en cadenas largas con muchos reemplazos pequeños.

source = "Here is foo, it does moo!"

replacements = {
    'is': 'was', # replace 'is' with 'was'
    'does': 'did',
    '!': '?'
}

def replace(source, replacements):
    finder = re.compile("|".join(re.escape(k) for k in replacements.keys())) # matches every string we want replaced
    result = []
    pos = 0
    while True:
        match = finder.search(source, pos)
        if match:
            # cut off the part up until match
            result.append(source[pos : match.start()])
            # cut off the matched part and replace it in place
            result.append(replacements[source[match.start() : match.end()]])
            pos = match.end()
        else:
            # the rest after the last match
            result.append(source[pos:])
            break
    return "".join(result)

print replace(source, replacements)

El punto es evitar muchas concatenaciones de cadenas largas. Cortamos la cadena de origen en fragmentos, reemplazando algunos de los fragmentos a medida que formamos la lista, y luego unimos todo de nuevo en una cadena.

9000
fuente
2

Realmente no deberías hacerlo de esta manera, pero me parece demasiado genial:

>>> replacements = {'cond1':'text1', 'cond2':'text2'}
>>> cmd = 'answer = s'
>>> for k,v in replacements.iteritems():
>>>     cmd += ".replace(%s, %s)" %(k,v)
>>> exec(cmd)

Ahora, answeres el resultado de todos los reemplazos a su vez.

De nuevo, esto es muy hacky y no es algo que debas usar regularmente. Pero es bueno saber que puedes hacer algo como esto si alguna vez lo necesitas.

inspectorG4dget
fuente
2

Estaba luchando con este problema también. Con muchas sustituciones, las expresiones regulares luchan y son aproximadamente cuatro veces más lentas que los bucles string.replace(en las condiciones de mi experimento).

Debería intentar usar la biblioteca Flashtext ( publicación de blog aquí , Github aquí ). En mi caso , fue un poco más de dos órdenes de magnitud más rápido, de 1.8 sa 0.015 s (las expresiones regulares tomaron 7.7 s) para cada documento.

Es fácil encontrar ejemplos de uso en los enlaces anteriores, pero este es un ejemplo de trabajo:

    from flashtext import KeywordProcessor
    self.processor = KeywordProcessor(case_sensitive=False)
    for k, v in self.my_dict.items():
        self.processor.add_keyword(k, v)
    new_string = self.processor.replace_keywords(string)

Nota que Flashtext hace sustituciones en una sola pasada (para evitar a -> b y b -> c traducción 'a' en 'c'). Flashtext también busca palabras completas (por lo que 'es' no coincidirá con ' es '). Funciona bien si su objetivo son varias palabras (reemplazando 'Esto es' por 'Hola').

Pablo
fuente
¿Cómo funciona esto si necesita reemplazar etiquetas HTML? Por ejemplo, reemplazar <p>con /n. ¿Intenté tu enfoque pero con las etiquetas flashtext no parece analizarlo?
alias51
1
No estoy seguro de por qué no funciona como esperabas. Una posibilidad es que estas etiquetas no estén separadas por espacios, y recuerde que Flashtext busca palabras completas. Una forma de evitar esto es usar un reemplazo simple primero, de modo que "Hola <p> allí" se convierta en "Hola <p> allí". Debería tener cuidado de eliminar los espacios no deseados cuando haya terminado (¿también reemplazar?). Espero que ayude.
Pablo
Gracias, ¿puedes configurar <y >marcar el final de una palabra (pero ser incluido en el reemplazo)?
alias51
1
Creo que las "palabras" están marcadas solo por espacios. Quizás hay algunos parámetros opcionales que puede establecer en "KeywordProcessor". De lo contrario, considere el enfoque anterior: sustituya "<" por "<", aplique Flashtext y luego sustituya de nuevo (en su caso, por ejemplo, "<" a "<" y "\ n" a "\ n" podrían funcionar).
Pablo
2

Siento que esta pregunta necesita una respuesta de función lambda recursiva de una sola línea para completar, solo porque. Por lo tanto, allí:

>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.popitem()), d)

Uso:

>>> mrep('abcabc', {'a': '1', 'c': '2'})
'1b21b2'

Notas:

  • Esto consume el diccionario de entrada.
  • Python dicts preserva el orden de las claves a partir de 3.6; Las advertencias correspondientes en otras respuestas ya no son relevantes. Por compatibilidad con versiones anteriores, se podría recurrir a una versión basada en tuplas:
>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.pop()), d)
>>> mrep('abcabc', [('a', '1'), ('c', '2')])

Nota: Al igual que con todas las funciones recursivas en Python, una profundidad de recursión demasiado grande (es decir, diccionarios de reemplazo demasiado grandes) dará como resultado un error. Ver, por ejemplo, aquí .

Mcsoini
fuente
Me encuentro con RecursionError cuando uso un diccionario grande.
Pablo
@Pablo Interesante. ¿Que tan grande? Tenga en cuenta que esto sucede para todas las funciones recursivas. Vea, por ejemplo, aquí: stackoverflow.com/questions/3323001/…
mcsoini
Mi diccionario de sustituciones está cerca de 100k términos ... hasta ahora, usar string.replace es, con mucho, el mejor enfoque.
Pablo
1
@Pablo en ese caso no puedes usar funciones recursivas. En general, sys.getrecursionlimit()es una pareja 1000, máx. use un bucle o algo así, o intente simplificar las sustituciones.
mcsoini
Sí, me temo que realmente no hay atajos aquí.
Pablo
1

No sé acerca de la velocidad, pero esta es mi solución rápida para el día de trabajo:

reduce(lambda a, b: a.replace(*b)
    , [('o','W'), ('t','X')] #iterable of pairs: (oldval, newval)
    , 'tomato' #The string from which to replace values
    )

... pero me gusta la respuesta # 1 de expresiones regulares anterior. Nota: si un nuevo valor es una subcadena de otro, entonces la operación no es conmutativa.

del_hol
fuente
1

Puede usar la pandasbiblioteca y la replacefunción que admite coincidencias exactas y reemplazos de expresiones regulares. Por ejemplo:

df = pd.DataFrame({'text': ['Billy is going to visit Rome in November', 'I was born in 10/10/2010', 'I will be there at 20:00']})

to_replace=['Billy','Rome','January|February|March|April|May|June|July|August|September|October|November|December', '\d{2}:\d{2}', '\d{2}/\d{2}/\d{4}']
replace_with=['name','city','month','time', 'date']

print(df.text.replace(to_replace, replace_with, regex=True))

Y el texto modificado es:

0    name is going to visit city in month
1                      I was born in date
2                 I will be there at time

Puedes encontrar un ejemplo aquí . Observe que los reemplazos en el texto se realizan con el orden en que aparecen en las listas

George Pipis
fuente
1

Para reemplazar solo un personaje, use translatey str.maketranses mi método favorito.

tl; dr> result_string = your_string.translate(str.maketrans(dict_mapping))


manifestación

my_string = 'This is a test string.'
dict_mapping = {'i': 's', 's': 'S'}
result_good = my_string.translate(str.maketrans(dict_mapping))
result_bad = my_string
for x, y in dict_mapping.items():
    result_bad = result_bad.replace(x, y)
print(result_good)  # ThsS sS a teSt Strsng.
print(result_bad)   # ThSS SS a teSt StrSng.
Carson
fuente
0

A partir de la preciosa respuesta de Andrew, desarrollé un script que carga el diccionario de un archivo y elabora todos los archivos en la carpeta abierta para hacer los reemplazos. El script carga las asignaciones de un archivo externo en el que puede establecer el separador. Soy un principiante, pero este script me pareció muy útil al hacer múltiples sustituciones en múltiples archivos. Cargó un diccionario con más de 1000 entradas en segundos. No es elegante pero funcionó para mí.

import glob
import re

mapfile = input("Enter map file name with extension eg. codifica.txt: ")
sep = input("Enter map file column separator eg. |: ")
mask = input("Enter search mask with extension eg. 2010*txt for all files to be processed: ")
suff = input("Enter suffix with extension eg. _NEW.txt for newly generated files: ")

rep = {} # creation of empy dictionary

with open(mapfile) as temprep: # loading of definitions in the dictionary using input file, separator is prompted
    for line in temprep:
        (key, val) = line.strip('\n').split(sep)
        rep[key] = val

for filename in glob.iglob(mask): # recursion on all the files with the mask prompted

    with open (filename, "r") as textfile: # load each file in the variable text
        text = textfile.read()

        # start replacement
        #rep = dict((re.escape(k), v) for k, v in rep.items()) commented to enable the use in the mapping of re reserved characters
        pattern = re.compile("|".join(rep.keys()))
        text = pattern.sub(lambda m: rep[m.group(0)], text)

        #write of te output files with the prompted suffice
        target = open(filename[:-4]+"_NEW.txt", "w")
        target.write(text)
        target.close()
Tommaso Sandi
fuente
0

Esta es mi solución al problema. Lo usé en un chatbot para reemplazar las diferentes palabras a la vez.

def mass_replace(text, dct):
    new_string = ""
    old_string = text
    while len(old_string) > 0:
        s = ""
        sk = ""
        for k in dct.keys():
            if old_string.startswith(k):
                s = dct[k]
                sk = k
        if s:
            new_string+=s
            old_string = old_string[len(sk):]
        else:
            new_string+=old_string[0]
            old_string = old_string[1:]
    return new_string

print mass_replace("The dog hunts the cat", {"dog":"cat", "cat":"dog"})

esto se convertirá The cat hunts the dog

emorjon2
fuente
0

Otro ejemplo: lista de entrada

error_list = ['[br]', '[ex]', 'Something']
words = ['how', 'much[ex]', 'is[br]', 'the', 'fish[br]', 'noSomething', 'really']

La salida deseada sería

words = ['how', 'much', 'is', 'the', 'fish', 'no', 'really']

Código:

[n[0][0] if len(n[0]) else n[1] for n in [[[w.replace(e,"") for e in error_list if e in w],w] for w in words]] 
Akhil Thayyil
fuente
-2

O simplemente para un truco rápido:

for line in to_read:
    read_buffer = line              
    stripped_buffer1 = read_buffer.replace("term1", " ")
    stripped_buffer2 = stripped_buffer1.replace("term2", " ")
    write_to_file = to_write.write(stripped_buffer2)
Brandon H
fuente
-2

Aquí hay otra forma de hacerlo con un diccionario:

listA="The cat jumped over the house".split()
modify = {word:word for number,word in enumerate(listA)}
modify["cat"],modify["jumped"]="dog","walked"
print " ".join(modify[x] for x in listA)
Stefan Gruenwald
fuente