Wie ersetze ich mehrere Teilzeichenfolgen eines Strings?


284

Ich möchte die .replace-Funktion verwenden, um mehrere Zeichenfolgen zu ersetzen.

Ich habe derzeit

string.replace("condition1", "")

möchte aber so etwas haben

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

obwohl sich das nicht nach guter Syntax anfühlt

Was ist der richtige Weg, um dies zu tun? Art wie wie in grep / regex Sie tun können , \1und \2Felder auf bestimmte Suchzeichenketten zu ersetzen


7
Haben Sie alle angebotenen Lösungen ausprobiert? Welches ist schneller?
Tommy.carstensen

Ich habe mir die Zeit genommen, alle Antworten in verschiedenen Szenarien zu testen. Siehe stackoverflow.com/questions/59072514/…
Pablo

1
Ehrlich gesagt, ich bevorzuge Ihren verketteten Ansatz gegenüber allen anderen. Ich bin hier gelandet, als ich nach einer Lösung gesucht und deine verwendet habe, und es funktioniert einwandfrei.
frakman1

@ frakman1 +1. Keine Ahnung, warum dies nicht mehr positiv bewertet wird. Alle anderen Methoden erschweren das Lesen von Code. Wenn es zu ersetzende Funktionsübergabearrays gäbe, würde dies funktionieren. Ihre verkettete Methode ist jedoch am
klarsten

Antworten:


269

Hier ist ein kurzes Beispiel, das den Trick mit regulären Ausdrücken machen sollte:

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)

Beispielsweise:

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

7
Der Austausch erfolgt in einem Durchgang.
Andrew Clark

26
dkamins: es ist nicht zu klug, es ist nicht einmal so klug, wie es sein sollte (wir sollten die Schlüssel regex-entkommen, bevor wir sie mit "|" verbinden). Warum ist das nicht überarbeitet? weil auf diese Weise wir es in einem Durchgang (= schnell) zu tun, und wir zugleich alle die Ersetzungen zu tun, Auseinandersetzungen wie zu vermeiden "spamham sha".replace("spam", "eggs").replace("sha","md5")ist , "eggmd5m md5"statt"eggsham md5"
fliegt Schaf

8
@ AndrewClark Ich würde mich sehr freuen, wenn Sie erklären könnten, was in der letzten Zeile mit Lambda passiert.
Mineralien

11
Hallo, ich habe einen kleinen Kern mit einer klareren Version dieses Snippets erstellt. Es sollte auch etwas effizienter sein: gist.github.com/bgusach/a967e0587d6e01e889fd1d776c5f3729
bgusach

15
Verwenden Sie für Python 3 items () anstelle von iteritems ().
Jangari

127

Sie könnten einfach eine nette kleine Schleifenfunktion machen.

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

Dabei texthandelt es sich um die vollständige Zeichenfolge und dicein Wörterbuch. Jede Definition ist eine Zeichenfolge, die eine Übereinstimmung mit dem Begriff ersetzt.

Hinweis : In Python 3 iteritems()wurde durch ersetztitems()


Achtung: Python-Wörterbücher haben keine zuverlässige Reihenfolge für die Iteration. Diese Lösung löst Ihr Problem nur, wenn:

  • Die Reihenfolge des Austauschs spielt keine Rolle
  • Es ist in Ordnung, wenn ein Ersatz die Ergebnisse früherer Ersetzungen ändert

Zum Beispiel:

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

Mögliche Ausgabe Nr. 1:

"Das ist mein Schwein und das ist mein Schwein."

Mögliche Ausgabe # 2

"Das ist mein Hund und das ist mein Schwein."

Eine mögliche Lösung ist die Verwendung eines 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)

Ausgabe:

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

Vorsicht # 2: Ineffizient, wenn Ihre textZeichenfolge zu groß ist oder das Wörterbuch viele Paare enthält.


37
Die Reihenfolge, in der Sie die verschiedenen Ersetzungen anwenden, spielt eine Rolle. Anstatt ein Standarddiktat zu verwenden, sollten Sie ein OrderedDict- oder eine Liste mit 2 Tupeln verwenden.
Faultier

5
Dies macht das zweimalige Wiederholen der Zeichenfolge ... nicht gut für Auftritte.
Valentin Lorentz

6
In Bezug auf die Leistung ist es schlechter als das, was Valentin sagt - es wird den Text so oft durchlaufen, wie es Elemente in dic gibt! Gut, wenn 'Text' klein ist, aber für großen Text schrecklich.
JDonner

3
Dies ist in einigen Fällen eine gute Lösung. Zum Beispiel möchte ich nur 2 Zeichen unterschreiben, und die Reihenfolge, in der sie eingegeben werden, ist mir egal, da die Ersetzungsschlüssel keinen Werten entsprechen. Aber ich möchte, dass klar ist, was passiert.
Nathan Garabedian

5
Beachten Sie, dass dies zu unerwarteten Ergebnissen führen kann, da der neu eingefügte Text in der ersten Iteration in der zweiten Iteration abgeglichen werden kann. Wenn wir beispielsweise naiv versuchen, alle 'A' durch 'B' und alle 'B' durch 'C' zu ersetzen, wird die Zeichenfolge 'AB' in 'CC' und nicht in 'BC' umgewandelt.
Ambroz Bizjak

105

Warum nicht eine solche Lösung?

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
Dies ist sehr nützlich, einfach und tragbar.
Shred

Sah gut aus, ersetzte aber keinen regulären Ausdruck wie in: für r in ((r '\ s.', '.'), (R '\ s,', ',')):
Martin

2
um es zu einem 1-Liner zu machen: ss = [s.replace (* r) für r in (("braun", "rot"), ("faul", "schnell"))] [0]
Mark K

94

Hier ist eine Variante der ersten Lösung mit Reduce, falls Sie funktionsfähig sein möchten. :) :)

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

Martineaus noch bessere Version:

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

8
Es wäre einfacher, replseine Folge von Tupeln zu erstellen und den iteritems()Anruf zu beenden. dh repls = ('hello', 'goodbye'), ('world', 'earth')und reduce(lambda a, kv: a.replace(*kv), repls, s). Würde auch unverändert in Python 3
funktionieren

nett! Wenn Sie Python3 verwenden, verwenden Sie Elemente anstelle von Iteritems (jetzt in Dicts entfernt).
e.arbitrio

2
@martineau: Es ist nicht wahr, dass dies in Python3 unverändert funktioniert, da reducees entfernt wurde .
Normanius

5
@normanius: reduceexistiert noch, wurde jedoch in Python 3 zu einem Teil des functoolsModuls (siehe Dokumentation ) gemacht. Als ich also unverändert sagte, meinte ich, dass derselbe Code ausgeführt werden könnte - obwohl dies zugegebenermaßen erfordern würde, dass reduceer importbei Bedarf bearbeitet wurde da es nicht mehr eingebaut ist.
Martineau

35

Dies ist nur eine präzisere Zusammenfassung der großartigen Antworten von FJ und MiniQuark. Alles, was Sie benötigen, um mehrere Zeichenfolgen gleichzeitig zu ersetzen, ist die folgende Funktion:

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)

Verwendungszweck:

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

Wenn Sie möchten, können Sie ausgehend von dieser einfacheren Funktion Ihre eigenen Ersatzfunktionen erstellen.


1
Dies ist zwar eine gute Lösung, aber das gleichzeitige Ersetzen von Zeichenfolgen führt nicht zu genau den gleichen Ergebnissen wie das sequentielle Durchführen (Verketten) - obwohl dies möglicherweise keine Rolle spielt.
Martineau

2
Sicher, mit rep_dict = {"but": "mut", "mutton": "lamb"}der Zeichenfolge "button"ergibt sich "mutton"Ihr Code, aber würde geben, "lamb"wenn die Ersetzungen nacheinander verkettet würden .
Martineau

2
Das ist das Hauptmerkmal dieses Codes, kein Defekt. Mit verketteten Ersetzungen konnte es nicht das gewünschte Verhalten erreichen, zwei Wörter gleichzeitig und wechselseitig zu ersetzen, wie in meinem Beispiel.
mmj

1
Es könnte keine großartige Funktion sein, wenn Sie es nicht brauchen. Aber hier geht es um gleichzeitiges Ersetzen , dann ist es in der Tat das Hauptmerkmal. Bei "verketteten" Ersetzungen wäre die Ausgabe des Beispiels Do you prefer cafe? No, I prefer cafe., was überhaupt nicht wünschenswert ist.
mmj

@ David schreiben Sie Ihre eigene Antwort, Ihre Bearbeitung ist zu radikal
UmNyobe

29

Ich habe dies auf FJs ausgezeichneter Antwort aufgebaut:

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)

One-Shot-Verwendung:

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

Da der Austausch in nur einem Durchgang erfolgt, ändert sich "Café" in "Tee", jedoch nicht wieder in "Café".

Wenn Sie denselben Austausch mehrmals durchführen müssen, können Sie einfach eine Ersatzfunktion erstellen:

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

Verbesserungen:

  • verwandelte Code in eine Funktion
  • Mehrzeilige Unterstützung hinzugefügt
  • Ein Fehler beim Entkommen wurde behoben
  • Einfach, eine Funktion für einen bestimmten Mehrfachersatz zu erstellen

Genießen! :-)


1
Könnte jemand dies Schritt für Schritt für Python-Noobs wie mich erklären?
Julian Suarez

Python Noob hier, also werde ich einen unvollständigen Versuch unternehmen, es zu verstehen. A. Zerlegen Sie key_values ​​in zu ersetzende Elemente (Schlüssel, die durch "|" verbunden sind) und Logik (wenn die Übereinstimmung ein Schlüssel ist, geben Sie den Wert zurück). b. Erstellen Sie einen Regex-Parser ("Muster", das nach Schlüsseln sucht und die angegebene Logik verwendet) - wickeln Sie dies in eine Lambda-Funktion ein und kehren Sie zurück. Sachen, die ich jetzt nachschaue: re.M. und die Notwendigkeit von Lambda als Ersatzlogik.
Fox

1
@ Fox Du hast es verstanden. Sie können eine Funktion definieren, anstatt ein Lambda zu verwenden. Dies dient lediglich dazu, den Code zu verkürzen. Beachten Sie jedoch, pattern.subdass eine Funktion mit nur einem Parameter (dem zu ersetzenden Text) erwartet wird, sodass die Funktion Zugriff haben muss replace_dict. re.MErmöglicht mehrzeilige Ersetzungen (dies wird im Dokument gut erklärt: docs.python.org/2/library/re.html#re.M ).
MiniQuark

22

Ich möchte die Verwendung von Zeichenfolgenvorlagen vorschlagen. Platzieren Sie einfach die zu ersetzende Zeichenfolge in einem Wörterbuch und alles ist festgelegt! Beispiel aus 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'

Sieht gut aus, aber wenn Sie einen Schlüssel hinzufügen, der nicht in enthalten ist, wird substituteeine Ausnahme ausgelöst. Seien Sie also vorsichtig, wenn Sie Vorlagen von Benutzern erhalten.
Bart Friederichs

2
Ein Nachteil dieses Ansatzes ist, dass die Vorlage alle und nicht mehr als alle $ Zeichenfolgen enthalten muss, um ersetzt zu werden, siehe hier
RolfBly

17

In meinem Fall brauchte ich ein einfaches Ersetzen eindeutiger Schlüssel durch Namen, also dachte ich mir Folgendes aus:

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

3
Dies funktioniert, solange Sie keinen Ersatzkonflikt haben. Wenn Sie ersetzt imit sIhnen würde ein seltsames Verhalten bekommen.
Bgusach

1
Wenn die Reihenfolge von Bedeutung ist, können Sie anstelle des obigen Diktats ein Array verwenden: b = [ ['i', 'Z'], ['s', 'Y'] ]; for x,y in (b): a = a.replace(x, y) Wenn Sie Ihre Array-Paare sorgfältig bestellen, können Sie sicherstellen, dass Sie () nicht rekursiv ersetzen.
CODE-REaD

Es scheint, dass Diktate jetzt die Reihenfolge von Python 3.7.0 beibehalten . Ich habe es getestet und es funktioniert in Ordnung auf meinem Computer mit dem neuesten stabilen Python 3.
James Koss

15

Ab Python 3.8und Einführung von Zuweisungsausdrücken (PEP 572) ( :=Operator) können wir die Ersetzungen innerhalb eines Listenverständnisses anwenden:

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

Wissen Sie, ob dies effizienter ist als das Ersetzen in einer Schleife? Ich teste alle Antworten auf Leistung, habe aber noch keine 3.8.
Pablo

Warum bekomme ich die Ausgabe in einer Liste?
Johnrao07

1
@ johnrao07 Nun, ein Listenverständnis baut eine Liste auf. Deshalb bekommen Sie in diesem Fall ['The quick red fox jumps over the lazy dog', 'The quick red fox jumps over the quick dog']. Der Zuweisungsausdruck ( text := text.replace) erstellt aber auch iterativ neue Versionen von, textindem er ihn mutiert. Nach dem Listenverständnis können Sie die textVariable verwenden, die den geänderten Text enthält.
Xavier Guihot

1
Wenn Sie die neue Version von textals Einzeiler zurückgeben möchten , können Sie auch [text := text.replace(a, b) for a, b in replacements][-1](beachten Sie das [-1]) verwenden, das das letzte Element des Listenverständnisses extrahiert. dh die letzte Version von text.
Xavier Guihot

13

Hier meine $ 0,02. Es basiert auf Andrew Clarks Antwort, die nur ein wenig klarer ist, und deckt auch den Fall ab, dass eine zu ersetzende Zeichenfolge eine Teilzeichenfolge einer anderen zu ersetzenden Zeichenfolge ist (längere Zeichenfolge gewinnt).

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 diesem Kern können Sie es jederzeit ändern, wenn Sie einen Vorschlag haben.


1
Dies sollte stattdessen die akzeptierte Antwort gewesen sein, da der reguläre Ausdruck aus allen Schlüsseln aufgebaut ist, indem sie in absteigender Reihenfolge der Länge sortiert und mit | verknüpft werden Regex-Wechseloperator. Und die Sortierung ist notwendig, damit die längste aller möglichen Auswahlmöglichkeiten ausgewählt wird, wenn es Alternativen gibt.
Sachin S

Ich bin damit einverstanden, dass dies dank der Sortierung die beste Lösung ist. Abgesehen davon, dass die Sortierung mit meiner ursprünglichen Antwort identisch ist, habe ich die Sortierung auch für meine Lösung ausgeliehen, um sicherzustellen, dass niemand eine so wichtige Funktion verpasst.
mmj

6

Ich brauchte eine Lösung, bei der die zu ersetzenden Zeichenfolgen reguläre Ausdrücke sein können, um beispielsweise einen langen Text zu normalisieren, indem mehrere Leerzeichen durch ein einzelnes ersetzt werden. Aufbauend auf einer Reihe von Antworten anderer, einschließlich MiniQuark und mmj, habe ich mir Folgendes ausgedacht:

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)

Es funktioniert für die Beispiele in anderen Antworten, zum Beispiel:

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

Die Hauptsache für mich ist, dass Sie auch reguläre Ausdrücke verwenden können, um beispielsweise nur ganze Wörter zu ersetzen oder Leerzeichen zu normalisieren:

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

Wenn Sie die Wörterbuchschlüssel als normale Zeichenfolgen verwenden möchten, können Sie diese vor dem Aufruf von multiple_replace mit folgender Funktion umgehen:

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"

Die folgende Funktion kann dabei helfen, fehlerhafte reguläre Ausdrücke in Ihren Wörterbuchschlüsseln zu finden (da die Fehlermeldung von multiple_replace nicht sehr aussagekräftig ist):

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

Beachten Sie, dass die Ersetzungen nicht verkettet, sondern gleichzeitig ausgeführt werden. Dies macht es effizienter, ohne die Möglichkeiten einzuschränken. Um den Effekt der Verkettung nachzuahmen, müssen Sie möglicherweise nur weitere Zeichenfolgenersatzpaare hinzufügen und die erwartete Reihenfolge der Paare sicherstellen:

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

Das ist schön, danke. Könnte es verbessert werden, dass auch Rückverweise in den Substitutionen verwendet werden können? Ich habe nicht sofort herausgefunden, wie ich das hinzufügen soll.
Cmarqu

Die Antwort auf meine Frage oben ist stackoverflow.com/questions/45630940/…
cmarqu

4

Hier ist ein Beispiel, das bei langen Saiten mit vielen kleinen Ersetzungen effizienter ist.

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)

Es geht darum, viele Verkettungen langer Zeichenfolgen zu vermeiden. Wir zerlegen die Quellzeichenfolge in Fragmente, ersetzen einige der Fragmente, während wir die Liste bilden, und verbinden dann das Ganze wieder zu einer Zeichenfolge.


2

Du solltest es wirklich nicht so machen, aber ich finde es einfach viel zu cool:

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

Nun answerist das Ergebnis aller Ersetzungen der Reihe nach

Auch dies ist sehr hackig und sollte nicht regelmäßig verwendet werden. Aber es ist nur schön zu wissen, dass Sie so etwas tun können, wenn Sie es jemals brauchen.


2

Ich hatte auch mit diesem Problem zu kämpfen. Bei vielen Substitutionen kämpfen reguläre Ausdrücke und sind ungefähr viermal langsamer als Schleifen string.replace(unter meinen Versuchsbedingungen).

Sie sollten unbedingt versuchen, die Flashtext- Bibliothek zu verwenden ( Blog-Beitrag hier , Github hier ). In meinem Fall war es etwas mehr als zwei Größenordnungen schneller, von 1,8 s bis 0,015 s (reguläre Ausdrücke dauerten 7,7 s). für jedes Dokument.

Es ist leicht, Anwendungsbeispiele in den obigen Links zu finden, aber dies ist ein funktionierendes Beispiel:

    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)

Beachten Sie, dass Flashtext Ersetzungen in einem einzigen Durchgang vornimmt (um zu vermeiden, dass a -> b und b -> c 'a' in 'c' übersetzen). Flashtext sucht auch nach ganzen Wörtern (also stimmt 'is' nicht mit 'th is ' überein ). Es funktioniert gut, wenn Ihr Ziel aus mehreren Wörtern besteht (indem Sie "Dies ist" durch "Hallo" ersetzen).


Wie funktioniert das, wenn Sie HTML-Tags ersetzen müssen? ZB ersetzen <p>durch /n. Ich habe Ihren Ansatz ausprobiert, aber mit Tags scheint Flashtext ihn nicht zu analysieren?
alias51

1
Ich bin mir nicht sicher, warum es nicht so funktioniert, wie Sie es erwarten. Eine Möglichkeit besteht darin, dass diese Tags nicht durch Leerzeichen getrennt sind und Flashtext nach ganzen Wörtern sucht. Eine Möglichkeit, dies zu umgehen, besteht darin, zuerst ein einfaches Ersetzen zu verwenden, sodass "Hi <p> ​​there" zu "Hi <p> ​​there" wird. Sie müssen vorsichtig sein, um unerwünschte Leerzeichen zu entfernen, wenn Sie fertig sind (auch einfach ersetzen?). Hoffentlich hilft das.
Pablo

Danke, können Sie das Ende eines Wortes setzen <und >markieren (aber beim Ersetzen enthalten sein)?
alias51

1
Ich glaube, dass "Wörter" nur durch Leerzeichen gekennzeichnet sind. Möglicherweise gibt es einige optionale Parameter, die Sie in "KeywordProcessor" festlegen können. Andernfalls sollten Sie den obigen Ansatz berücksichtigen: Ersetzen Sie "<" durch "<", wenden Sie Flashtext an und ersetzen Sie dann zurück (in Ihrem Fall könnte beispielsweise "<" bis "<" und "\ n" bis "\ n" funktionieren).
Pablo

2

Ich bin der Meinung, dass diese Frage der Vollständigkeit halber eine einzeilige Antwort auf die rekursive Lambda-Funktion benötigt, nur weil. Also da:

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

Verwendungszweck:

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

Anmerkungen:

  • Dies verbraucht das Eingabewörterbuch.
  • Python-Dikte behalten die Schlüsselreihenfolge ab 3.6 bei. entsprechende Vorbehalte in anderen Antworten sind nicht mehr relevant. Aus Gründen der Abwärtskompatibilität könnte man auf eine Tupel-basierte Version zurückgreifen:
>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.pop()), d)
>>> mrep('abcabc', [('a', '1'), ('c', '2')])

Hinweis: Wie bei allen rekursiven Funktionen in Python führt eine zu große Rekursionstiefe (dh zu große Ersatzwörterbücher) zu einem Fehler. Siehe zB hier .


Ich stoße auf RecursionError, wenn ich ein großes Wörterbuch verwende!
Pablo

@Pablo Interessant. Wie groß? Beachten Sie, dass dies für alle rekursiven Funktionen gilt. Siehe zum Beispiel hier: stackoverflow.com/questions/3323001/…
mcsoini

Mein Substitutionswörterbuch umfasst fast 100.000 Begriffe. Bisher ist die Verwendung von string.replace bei weitem der beste Ansatz.
Pablo

1
@Pablo In diesem Fall können Sie keine rekursiven Funktionen verwenden. Im Allgemeinen sys.getrecursionlimit()ist ein Paar 1000, max. Verwenden Sie eine Schleife oder ähnliches oder versuchen Sie, die Ersetzungen zu vereinfachen.
McSoini

Ja, ich fürchte, hier gibt es wirklich keine Abkürzung.
Pablo

1

Ich weiß nichts über Geschwindigkeit, aber dies ist meine tägliche schnelle Lösung:

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

... aber ich mag die # 1 Regex Antwort oben. Hinweis - Wenn ein neuer Wert eine Teilzeichenfolge eines anderen ist, ist die Operation nicht kommutativ.


1

Sie können die pandasBibliothek und die replaceFunktion verwenden, die sowohl exakte Übereinstimmungen als auch Regex-Ersetzungen unterstützt. Zum Beispiel:

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

Und der geänderte Text lautet:

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

Ein Beispiel finden Sie hier . Beachten Sie, dass die Ersetzungen im Text in der Reihenfolge erfolgen, in der sie in den Listen aufgeführt sind


1

Um nur ein Zeichen zu ersetzen, verwenden Sie das translateund str.maketransist meine Lieblingsmethode.

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

Ausgehend von der wertvollen Antwort von Andrew entwickelte ich ein Skript, das das Wörterbuch aus einer Datei lädt und alle Dateien im geöffneten Ordner ausarbeitet, um die Ersetzungen vorzunehmen. Das Skript lädt die Zuordnungen aus einer externen Datei, in der Sie das Trennzeichen festlegen können. Ich bin ein Anfänger, aber ich fand dieses Skript sehr nützlich, wenn ich mehrere Ersetzungen in mehreren Dateien vornehme. Es wurde ein Wörterbuch mit mehr als 1000 Einträgen in Sekunden geladen. Es ist nicht elegant, aber es hat bei mir funktioniert

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

Das ist meine Lösung für das Problem. Ich habe es in einem Chatbot verwendet, um die verschiedenen Wörter gleichzeitig zu ersetzen.

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

das wird werden The cat hunts the dog


0

Ein weiteres Beispiel: Eingabeliste

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

Die gewünschte Ausgabe wäre

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

Code:

[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

Oder nur für einen schnellen Hack:

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

Hier ist eine andere Möglichkeit, dies mit einem Wörterbuch zu tun:

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)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.