472 Stimmen

Wie kann man mehrere Teilstrings einer Zeichenkette ersetzen?

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

Ich habe derzeit

string.replace("condition1", "")

aber ich würde gerne etwas haben wie

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

obwohl sich das nicht nach einer guten Syntax anfühlt

Was ist der richtige Weg, um dies zu tun? ähnlich wie in grep/regex können Sie tun \1 y \2 um Felder durch bestimmte Suchbegriffe zu ersetzen

2 Stimmen

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

4 Stimmen

Die kurze Antwort lautet: Es gibt keinen besseren Weg, dies zu tun.

9voto

Ich brauchte eine Lösung, bei der die zu ersetzenden Zeichenfolgen reguläre Ausdrücke sein können, zum Beispiel, um einen langen Text zu normalisieren, indem mehrere Leerzeichen durch ein einziges ersetzt werden. Aufbauend auf einer Kette von Antworten von anderen, einschließlich MiniQuark und mmj, habe ich folgendes gefunden:

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 zum Beispiel für die in anderen Antworten genannten Beispiele:

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

Das Wichtigste für mich ist, dass man auch reguläre Ausdrücke verwenden kann, z. B. um nur ganze Wörter zu ersetzen oder um 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 Zeichenketten verwenden wollen, können Sie diese vor dem Aufruf von multiple_replace z. B. mit dieser Funktion entschlüsseln:

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 durchgeführt werden. Das macht sie effizienter, ohne ihre Möglichkeiten einzuschränken. Um den Effekt der Verkettung zu imitieren, müssen Sie vielleicht nur mehr Paare von Zeichenfolgen-Ersetzungen 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'

7voto

9000 Punkte 38520

Hinweis: Testen Sie Ihren Fall, siehe Kommentare.

Hier ist ein Beispiel, das bei langen Saiten mit vielen kleinen Auswechslungen 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 von langen Zeichenfolgen zu vermeiden. Wir zerlegen die Quellzeichenkette in Fragmente, wobei wir einige der Fragmente bei der Erstellung der Liste ersetzen, und fügen dann das Ganze wieder zu einer Zeichenkette zusammen.

6voto

George Pipis Punkte 1044

Sie können die pandas Bibliothek und die replace Funktion, 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 unter aquí . Beachten Sie, dass die Ersetzungen im Text in der Reihenfolge vorgenommen werden, in der sie in den Listen erscheinen

5voto

Ich habe eine ähnliche Übung in einer meiner Schulaufgaben gemacht. Dies war meine Lösung

dictionary = {1: ['hate', 'love'],
              2: ['salad', 'burger'],
              3: ['vegetables', 'pizza']}

def normalize(text):
    for i in dictionary:
        text = text.replace(dictionary[i][0], dictionary[i][1])
    return text

Sehen Sie das Ergebnis selbst auf dem Teststreifen

string_to_change = 'I hate salad and vegetables'
print(normalize(string_to_change))

1 Stimmen

Warum ist dictionary hier als Array verwendet wird? Was ist der Sinn der Verwendung von 1 , 2 y 3 als Schlüssel, obwohl Sie eine Liste hätten verwenden können? Ich denke, dass die Verwendung des "Vorher"-Werts als Schlüssel und des "Nachher"-Werts als Wert und das anschließende Durchlaufen des Wörterbuchs mit .items() wäre besser.

0 Stimmen

Es gibt viele Möglichkeiten, wie man es machen kann, meine ist keineswegs die beste oder optimalste. aber ich mag die Lesbarkeit, vielleicht ist es nur mein OCD, der alles beschriften/markieren muss. Ihr Ansatz ist auf jeden Fall praktikabel.

5voto

Pablo Punkte 1217

Auch ich hatte mit diesem Problem zu kämpfen. Mit vielen Ersetzungen haben reguläre Ausdrücke zu kämpfen und sind etwa viermal langsamer als die Schleifenbildung string.replace (unter meinen Versuchsbedingungen).

Sie sollten unbedingt versuchen, die Flashtext Bibliothek ( Blogbeitrag hier , Github hier ). In meinem Fall es war ein bisschen zu viel um zwei Größenordnungen schneller, von 1,8 s auf 0,015 s (reguläre Ausdrücke benötigten 7,7 s) für jedes Dokument.

In den obigen Links finden Sie leicht Anwendungsbeispiele, aber dies ist ein Arbeitsbeispiel:

    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 y b --> c Übersetzung von "a" in "c"). Flashtext sucht auch nach ganzen Wörtern (so wird 'is' nicht mit 'th ist '). Es funktioniert gut, wenn Ihr Ziel aus mehreren Wörtern besteht (Ersetzen von "Dies ist" durch "Hallo").

1 Stimmen

Ich bin mir nicht sicher, warum es nicht so funktioniert, wie Sie es erwarten. Eine Möglichkeit ist, dass diese Tags nicht durch Leerzeichen getrennt sind, und Flashtext sucht nach ganzen Wörtern. Eine Möglichkeit, dies zu umgehen, besteht darin, zunächst eine einfache Ersetzung vorzunehmen, so dass "Hi<p>there" zu "Hi <p> there" wird. Danach müssen Sie darauf achten, dass unerwünschte Leerzeichen entfernt werden (ebenfalls durch einfaches Ersetzen?). Ich hoffe, das hilft.

1 Stimmen

Ich glaube, dass "Wörter" nur durch Leerzeichen gekennzeichnet sind. Vielleicht gibt es einige optionale Parameter, die Sie in "KeywordProcessor" einstellen können. Andernfalls sollten Sie den obigen Ansatz in Betracht ziehen: Ersetzen Sie "<" durch " <", wenden Sie Flashtext an und ersetzen Sie dann wieder (in Ihrem Fall zum Beispiel " <" durch "<" und " \n " zu " \n " könnte funktionieren).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X