Come sostituire più sottostringhe di una stringa?


307

Vorrei utilizzare la funzione .replace per sostituire più stringhe.

Attualmente ho

string.replace("condition1", "")

ma vorrei avere qualcosa di simile

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

anche se non sembra una buona sintassi

qual è il modo corretto per farlo? un po 'come puoi fare in grep / regex \1e \2sostituire i campi con determinate stringhe di ricerca


8
Hai provato tutte le soluzioni fornite? Quale è più veloce?
tommy.carstensen

Mi sono preso il tempo per testare tutte le risposte in diversi scenari. Vedi stackoverflow.com/questions/59072514/…
Pablo

2
Onestamente, preferisco il tuo approccio incatenato a tutti gli altri. Sono atterrato qui mentre cercavo una soluzione e ho usato il tuo e funziona benissimo.
frakman1

@ frakman1 +1. nessun indizio sul motivo per cui questo non è più votato. Tutti gli altri metodi rendono il codice più difficile da leggere. Se ci fosse un array di passaggi di funzione da sostituire, funzionerebbe. Ma il tuo metodo concatenato è molto chiaro (almeno con un numero statico di sostituzioni)
IceFire il

Risposte:


275

Ecco un breve esempio che dovrebbe fare il trucco con le espressioni regolari:

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)

Per esempio:

>>> pattern.sub(lambda m: rep[re.escape(m.group(0))], "(condition1) and --condition2--")
'() and --text--'

7
La sostituzione avviene in un unico passaggio.
Andrew Clark

27
dkamins: non è troppo intelligente, non è nemmeno così intelligente come dovrebbe essere (dovremmo regex-escape le chiavi prima di unirle con "|"). perché non è troppo ingegnerizzato? perché in questo modo lo facciamo in una passata (= veloce), e facciamo tutte le sostituzioni contemporaneamente, evitando scontri come "spamham sha".replace("spam", "eggs").replace("sha","md5")essere "eggmd5m md5"invece di"eggsham md5"
pecora volante

8
@AndrewClark Apprezzerei molto se potessi spiegare cosa sta succedendo nell'ultima riga con lambda.
minerali

11
Salve, ho creato un piccolo riassunto con una versione più chiara di questo frammento. Dovrebbe essere anche leggermente più efficiente: gist.github.com/bgusach/a967e0587d6e01e889fd1d776c5f3729
bgusach

16
Per python 3, usa items () invece di iteritems ().
Jangari

137

Potresti semplicemente creare una piccola funzione di looping.

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

dove textè la stringa completa ed dicè un dizionario: ogni definizione è una stringa che sostituirà una corrispondenza con il termine.

Nota : in Python 3, iteritems()è stato sostituito conitems()


Attenzione: i dizionari Python non hanno un ordine affidabile per l'iterazione. Questa soluzione risolve il tuo problema solo se:

  • l'ordine delle sostituzioni è irrilevante
  • va bene per una sostituzione cambiare i risultati delle sostituzioni precedenti

Per esempio:

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

Possibile output # 1:

"Questo è il mio maiale e questo è il mio maiale."

Possibile uscita n. 2

"Questo è il mio cane e questo è il mio maiale."

Una possibile soluzione consiste nell'usare 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)

Produzione:

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

Attenzione n. 2: inefficiente se la tua textstringa è troppo grande o ci sono molte coppie nel dizionario.


37
L'ordine in cui applicherai le diverse sostituzioni sarà importante, quindi invece di usare un dict standard, considera l'utilizzo di un OrderedDict- o un elenco di 2-tuple.
slothrop

5
Questo rende l'iterazione della corda due volte ... non va bene per le performance.
Valentin Lorentz

7
Dal punto di vista delle prestazioni è peggio di quello che dice Valentin: attraverserà il testo tante volte quante sono le voci in dic! Va bene se "testo" è piccolo ma, terribile per testo grande.
JDonner

3
Questa è una buona soluzione per alcuni casi. Ad esempio, voglio solo sotto 2 caratteri e non mi interessa l'ordine in cui entrano perché le chiavi di sostituzione non corrispondono a nessun valore. Ma voglio che sia chiaro cosa sta succedendo.
Nathan Garabedian

6
Notare che questo può dare risultati imprevisti perché il testo appena inserito nella prima iterazione può essere trovato nella seconda iterazione. Ad esempio, se ingenuamente proviamo a sostituire tutto "A" con "B" e tutto "B" con "C", la stringa "AB" verrebbe trasformata in "CC" e non "BC".
Ambroz Bizjak

110

Perché non una soluzione come questa?

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

2
Questo è super utile, semplice e portatile.
Shred

Sembrava carino, ma non ha sostituito le espressioni regolari come in: for r in ((r '\ s.', '.'), (R '\ s,', ',')):
Martin

2
per renderlo di 1 riga: ss = [s.replace (* r) for r in (("brown", "red"), ("lazy", "quick"))] [0]
Mark K,

1
Ciò soffre del problema di ordinazione di qualsiasi replaceapproccio multiplo "abc"e le tue sostituzioni sono (("a", "b"), ("b", "a"))che potresti aspettarti "bac"ma ottieni "aac". Inoltre, c'è il problema delle prestazioni della scansione dell'intera stringa ogni volta per chiamata, quindi la complessità è almeno O(number of replacements * len(s)), oltre a qualsiasi corrispondenza dello schema di corde che si verifica sotto il cofano.
ggorlen

1
@MarkK questo è intelligente ma molto costoso dal punto di vista della memoria perché fa un elenco gigantesco di tutti i risultati intermedi solo per buttarli via al garbage collector. functools.reducesarebbe un po 'più rispettoso: reduce(lambda a, e: a.replace(*e), ("ab",), "abac"). In ogni caso, non consiglio fondamentalmente l'approccio (vedi commento sopra).
ggorlen

97

Ecco una variante della prima soluzione che utilizza riduci, nel caso ti piaccia essere funzionale. :)

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

La versione ancora migliore di martineau:

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

8
Sarebbe più semplice creare replsuna sequenza di tuple e farla finita con la iteritems()chiamata. cioè repls = ('hello', 'goodbye'), ('world', 'earth')e reduce(lambda a, kv: a.replace(*kv), repls, s).
Funzionerebbe

simpatico! se usi python3 usa oggetti invece di iteritems (ora rimossi in dicts).
e.arbitrio

2
@martineau: Non è vero che funziona in modo invariato in python3 poiché reduceè stato rimosso .
normanius

5
@normanius: reduceesiste ancora, tuttavia è stato fatto parte del functoolsmodulo (vedere la documentazione ) in Python 3, quindi quando ho detto invariato, intendevo che lo stesso codice poteva essere eseguito, anche se è necessario che reducesia stato modificato importse necessario poiché non è più un built-in.
martineau

Sintassi a parte, questa è fondamentalmente la stessa di molte altre soluzioni in questa pagina che soffrono di scarsa complessità temporale, problemi di ordinamento e comportamenti imprevisti nella sostituzione.
ggorlen

36

Questo è solo un riassunto più conciso delle ottime risposte di FJ e MiniQuark. Tutto ciò che serve per ottenere più sostituzioni di stringhe simultanee è la seguente funzione:

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)

Utilizzo:

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

Se lo desideri, puoi realizzare le tue funzioni di sostituzione dedicate partendo da questa più semplice.


1
Sebbene questa sia una buona soluzione, le sostituzioni di stringhe simultanee non daranno esattamente gli stessi risultati di eseguirle in sequenza (concatenandole), anche se potrebbe non avere importanza.
martineau

2
Certo, con rep_dict = {"but": "mut", "mutton": "lamb"}la stringa "button"risulta "mutton"con il tuo codice, ma darebbe "lamb"se le sostituzioni fossero concatenate, una dopo l'altra.
martineau

3
Questa è la caratteristica principale di questo codice, non un difetto. Con le sostituzioni concatenate non potrebbe ottenere il comportamento desiderato di sostituire due parole simultaneamente e reciprocamente come nel mio esempio.
mmj

1
Non potrebbe sembrare una grande funzionalità se non ne hai bisogno. Ma qui stiamo parlando di sostituzioni simultanee , quindi è proprio la caratteristica principale. Con sostituzioni "concatenate", l'output dell'esempio sarebbe Do you prefer cafe? No, I prefer cafe., il che non è affatto desiderabile.
mmj

@David scrivi la tua risposta, la tua modifica è troppo radicale
UmNyobe

28

Ho costruito questo sulla risposta eccellente di 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)

Utilizzo di un colpo:

>>> 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é.

Notare che poiché la sostituzione avviene in un solo passaggio, "café" cambia in "tea", ma non torna a "café".

Se è necessario eseguire la stessa sostituzione più volte, è possibile creare facilmente una funzione di sostituzione:

>>> 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!\"

Miglioramenti:

  • trasformato il codice in una funzione
  • aggiunto supporto multilinea
  • risolto un bug nell'escaping
  • facile creare una funzione per una specifica sostituzione multipla

Godere! :-)


1
Qualcuno potrebbe spiegare questo passo dopo passo per i niubbi Python come me?
Julian Suarez

Amico noob pitone qui, quindi farò un tentativo incompleto per capirlo .. a. scomporre i valori_chiave in cose da sostituire (chiavi unite da "|") e logica (se la corrispondenza è una chiave, restituisce il valore) b. crea un parser regex ("pattern" che cerca le chiavi e usa la logica data) - inseriscilo in una funzione lambda e ritorna. Cose che sto cercando ora: re.M e la necessità di lambda per la logica di sostituzione.
Fox

1
@Fox Hai capito. Potresti definire una funzione invece di usare un lambda, è solo per rendere il codice più breve. Ma nota che si pattern.subaspetta una funzione con un solo parametro (il testo da sostituire), quindi la funzione deve avere accesso a replace_dict. re.Mconsente le sostituzioni multilinea (è ben spiegato nel doc: docs.python.org/2/library/re.html#re.M ).
MiniQuark

22

Vorrei proporre l'utilizzo di modelli di stringa. Basta inserire la stringa da sostituire in un dizionario e tutto è pronto! Esempio da 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'

Sembra buono, ma quando si aggiunge una chiave non fornita in substitutesolleva un'eccezione, quindi fai attenzione quando ottieni i modelli dagli utenti.
Bart Friederichs

2
Uno svantaggio di questo approccio è che il modello deve contenere tutte e non più di tutte le stringhe $ da sostituire, vedere qui
RolfBly

17

Nel mio caso, avevo bisogno di una semplice sostituzione di chiavi univoche con nomi, quindi ho pensato questo:

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.'

5
Funziona finché non si dispone di un conflitto di sostituzione. Se sostituissi icon ste avresti un comportamento strano.
bgusach

1
Se l'ordine è significativo, invece del dict sopra puoi usare un array: b = [ ['i', 'Z'], ['s', 'Y'] ]; for x,y in (b): a = a.replace(x, y) quindi se stai attento a ordinare le tue coppie di array puoi assicurarti di non sostituire () ricorsivamente.
CODE-REaD

Sembra che i dict ora mantengano l'ordine , da Python 3.7.0. L'ho testato e funziona in ordine sulla mia macchina con l'ultima versione stabile di Python 3.
James Koss

In che modo è diverso dalla maggior parte delle altre risposte su questa pagina?
ggorlen

17

Iniziando Python 3.8e introducendo le espressioni di assegnazione (PEP 572) ( :=operatore), possiamo applicare le sostituzioni all'interno di una lista di comprensione:

# 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'

Sai se questo è più efficiente rispetto all'utilizzo della sostituzione in un ciclo? Sto testando tutte le risposte per le prestazioni ma non ho ancora la 3.8.
Pablo

1
Perché ottengo l'output in un elenco?
johnrao07

1
@ johnrao07 Beh, una lista di comprensione costruisce una lista. Ecco perché, in questo caso, ottieni ['The quick red fox jumps over the lazy dog', 'The quick red fox jumps over the quick dog']. Ma l'espressione di assegnazione ( text := text.replace) crea anche in modo iterativo nuove versioni di textmutandola. Dopo la comprensione della lista, puoi usare la textvariabile che contiene il testo modificato.
Xavier Guihot

1
Se vuoi restituire la nuova versione di textcome una riga singola, puoi anche usare [text := text.replace(a, b) for a, b in replacements][-1](nota il [-1]), che estrae l'ultimo elemento della comprensione della lista; cioè l'ultima versione di text.
Xavier Guihot

1
Questo è un enorme spreco di spazio se hai bisogno solo dell'ultimo elemento. Non utilizzare le comprensioni di elenco come riduttori , sebbene la risposta collegata non sia particolarmente efficiente o utile poiché soffre di problemi di ordinamento di sostituzione, così come questo.
ggorlen

13

Ecco i miei $ 0,02. Si basa sulla risposta di Andrew Clark, solo un po 'più chiara, e copre anche il caso in cui una stringa da sostituire è una sottostringa di un'altra stringa da sostituire (una stringa più lunga vince)

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)

È in questa sostanza , sentiti libero di modificarla se hai qualche proposta.


1
Questa avrebbe dovuto essere la risposta accettata invece perché la regex è costruita da tutte le chiavi ordinandole in ordine decrescente di lunghezza e unendole con | operatore di alternanza regex. E l'ordinamento è necessario in modo che venga selezionata la più lunga di tutte le scelte possibili se ci sono alternative.
Sachin S

Sono d'accordo che questa sia la soluzione migliore, grazie allo smistamento. A parte l'ordinamento è identico alla mia risposta originale, quindi ho preso in prestito l'ordinamento anche per la mia soluzione, per assicurarmi che nessuno si perda una caratteristica così importante.
mmj

6

Avevo bisogno di una soluzione in cui le stringhe da sostituire possono essere espressioni regolari, ad esempio per aiutare a normalizzare un testo lungo sostituendo più caratteri di spazi bianchi con uno solo. Basandomi su una catena di risposte da altri, inclusi MiniQuark e mmj, questo è quello che ho pensato:

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)

Funziona per gli esempi forniti in altre risposte, ad esempio:

>>> 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.'

La cosa principale per me è che puoi usare anche espressioni regolari, ad esempio per sostituire solo parole intere o per normalizzare lo spazio bianco:

>>> 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"

Se vuoi usare le chiavi del dizionario come stringhe normali, puoi evitarle prima di chiamare multiple_replace usando ad esempio questa funzione:

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 seguente funzione può aiutare a trovare espressioni regolari errate tra le chiavi del dizionario (poiché il messaggio di errore da multiple_replace non è molto indicativo):

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())

Si noti che non concatena le sostituzioni, ma le esegue contemporaneamente. Questo lo rende più efficiente senza limitare ciò che può fare. Per imitare l'effetto del concatenamento, potrebbe essere necessario aggiungere più coppie di sostituzione delle stringhe e garantire l'ordinamento previsto delle coppie:

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

È carino, grazie. Potrebbe essere migliorato per consentire anche l'uso di backreferences nelle sostituzioni? Non ho capito subito come aggiungerlo.
cmarqu

La risposta alla mia domanda di cui sopra è stackoverflow.com/questions/45630940/...
cmarqu

4

Nota: prova il tuo caso, vedi i commenti.

Ecco un esempio che è più efficiente su stringhe lunghe con molte piccole sostituzioni.

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)

Il punto è evitare molte concatenazioni di stringhe lunghe. Tagliamo la stringa sorgente in frammenti, sostituendo alcuni dei frammenti mentre formiamo l'elenco, quindi uniamo nuovamente l'intera cosa in una stringa.


Sono disponibili benchmark per supportare le asserzioni sulle prestazioni qui?
ggorlen

@ggorlen: In realtà il contrario: sulle stringhe entro i primi pochi kilobyte, la sostituzione e la concatenazione di stringhe lunghe è più veloce, secondo i miei test.
9000

2

Anch'io stavo lottando con questo problema. Con molte sostituzioni, le espressioni regolari fanno fatica e sono circa quattro volte più lente del ciclo string.replace(nelle condizioni del mio esperimento).

Dovresti assolutamente provare a utilizzare la libreria Flashtext ( post del blog qui , Github qui ). Nel mio caso è stato un po 'più di due ordini di grandezza più veloce, da 1,8 sa 0,015 s (le espressioni regolari impiegavano 7,7 s) per ogni documento.

È facile trovare esempi d'uso nei collegamenti sopra, ma questo è un esempio funzionante:

    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)

Notare che Flashtext rende sostituzioni in un unico passaggio (per evitare un -> b e b -> c traslante 'un' in 'c'). Flashtext cerca anche parole intere (quindi "è" non corrisponderà a "th è "). Funziona bene se il tuo obiettivo è composto da più parole (sostituendo "Questo è" con "Ciao").


Come funziona se devi sostituire i tag HTML? Ad esempio, sostituisci <p>con /n. Ho provato il tuo approccio ma con i tag flashtext non sembra analizzarlo?
alias51

1
Non sono sicuro del motivo per cui non funziona come previsto. Una possibilità è che questi tag non siano separati da spazi e ricorda che Flashtext cerca parole intere. Un modo per aggirare questo problema consiste nell'usare prima una semplice sostituzione, in modo che "Ciao <p> là" diventi "Ciao <p> là". Dovresti stare attento a rimuovere gli spazi indesiderati quando hai finito (anche semplice sostituzione?). Spero che aiuti.
Pablo

Grazie, puoi impostare <e >contrassegnare la fine di una parola (ma essere inclusa nella sostituzione)?
alias51

1
Credo che le "parole" siano contrassegnate solo da spazi. Forse ci sono alcuni parametri opzionali che puoi impostare in "KeywordProcessor". In caso contrario, considera l'approccio precedente: sostituire "<" con "<", applicare Flashtext quindi sostituire di nuovo (nel tuo caso, ad esempio, "<" a "<" e "\ n" a "\ n" potrebbero funzionare).
Pablo

2

Ritengo che questa domanda abbia bisogno di una risposta ricorsiva alla funzione lambda a riga singola per completezza, solo perché. Quindi ecco:

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

Utilizzo:

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

Appunti:

  • Questo consuma il dizionario di input.
  • I dict di Python mantengono l'ordine delle chiavi a partire da 3.6; i corrispondenti avvertimenti in altre risposte non sono più rilevanti. Per compatibilità con le versioni precedenti si potrebbe ricorrere a una versione basata su tuple:
>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.pop()), d)
>>> mrep('abcabc', [('a', '1'), ('c', '2')])

Nota: come con tutte le funzioni ricorsive in python, una profondità di ricorsione troppo grande (cioè dizionari di sostituzione troppo grandi) provocherà un errore. Vedi ad esempio qui .


Mi imbatto in RecursionError quando utilizzo un dizionario di grandi dimensioni!
Pablo

@Pablo interessante. Quanto largo? Notare che questo accade per tutte le funzioni ricorsive. Si veda ad esempio qui: stackoverflow.com/questions/3323001/...
mcsoini

Il mio dizionario delle sostituzioni è vicino a 100k termini ... finora l'uso di string.replace è di gran lunga l'approccio migliore.
Pablo

1
@ Pablo in questo caso non puoi usare funzioni ricorsive. In generale, sys.getrecursionlimit()è una coppia 1000, max. usa un ciclo o qualcosa del genere, oppure prova a semplificare le sostituzioni.
mcsoini

Sì, temo che non ci siano davvero scorciatoie qui.
Pablo

1

Non dovresti davvero farlo in questo modo, ma trovo che sia troppo bello:

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

Ora, answerè il risultato di tutte le sostituzioni a turno

ancora una volta, questo è molto complicato e non è qualcosa che dovresti usare regolarmente. Ma è solo bello sapere che puoi fare qualcosa del genere se ne hai bisogno.


1

Non conosco la velocità, ma questa è la mia soluzione rapida di tutti i giorni:

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

... ma mi piace la risposta regex n. 1 sopra. Nota: se un nuovo valore è una sottostringa di un altro, l'operazione non è commutativa.


1

È possibile utilizzare la pandaslibreria e la replacefunzione che supporta sia le corrispondenze esatte che le sostituzioni di espressioni regolari. Per esempio:

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))

E il testo modificato è:

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

Puoi trovare un esempio qui . Notare che le sostituzioni sul testo vengono eseguite nell'ordine in cui appaiono negli elenchi


1

Per sostituire un solo carattere, usa translatee str.maketransè il mio metodo preferito.

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


demo

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.

0

Partendo dalla preziosa risposta di Andrew ho sviluppato uno script che carica il dizionario da un file ed elabora tutti i file presenti nella cartella aperta per fare le sostituzioni. Lo script carica le mappature da un file esterno in cui è possibile impostare il separatore. Sono un principiante ma ho trovato questo script molto utile quando si eseguono più sostituzioni in più file. Ha caricato un dizionario con più di 1000 voci in pochi secondi. Non è elegante ma ha funzionato per me

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()

0

questa è la mia soluzione al problema. L'ho usato in un chatbot per sostituire le diverse parole contemporaneamente.

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"})

questo diventerà The cat hunts the dog


0

Un altro esempio: elenco di input

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

L'output desiderato sarebbe

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

Codice :

[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]] 

-2

O solo per un trucco veloce:

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)

-2

Ecco un altro modo per farlo con un dizionario:

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.