Der einfachste Weg, eine Zeichenfolge mithilfe eines Wörterbuchs mit Ersetzungen zu ersetzen?

74

Erwägen..

dict = {
'Спорт':'Досуг',
'russianA':'englishA'
}

s = 'Спорт russianA'

Ich möchte alle Diktatschlüssel durch ihre jeweiligen Diktatwerte in ersetzen s.

meder omuraliev
quelle
1
Dies ist möglicherweise nicht so einfach. Sie sollten wahrscheinlich einen expliziten Tokenizer haben (zum Beispiel {'cat': 'russiancat'}und "Raupe"). Auch überlappende Wörter ( {'car':'russiancar', 'pet' : 'russianpet'}und "Teppich").
Joe
2
Nebenbei: Ich denke, es dictwird am besten als Variablenname vermieden, da eine Variable dieses Namens die integrierte Funktion mit demselben Namen beschatten würde.
Jochen

Antworten:

98

Verwenden von re:

import re

s = 'Спорт not russianA'
d = {
'Спорт':'Досуг',
'russianA':'englishA'
}

pattern = re.compile(r'\b(' + '|'.join(d.keys()) + r')\b')
result = pattern.sub(lambda x: d[x.group()], s)
# Output: 'Досуг not englishA'

Dies entspricht nur ganzen Wörtern. Wenn Sie das nicht brauchen, verwenden Sie das Muster:

pattern = re.compile('|'.join(d.keys()))

Beachten Sie, dass Sie in diesem Fall die absteigenden Wörter nach Länge sortieren sollten, wenn einige Ihrer Wörterbucheinträge Teilzeichenfolgen anderer sind.

Max Shawabkeh
quelle
24
Falls die Wörterbuchschlüssel Zeichen wie "^", "$" und "/" enthalten, müssen die Schlüssel maskiert werden, bevor der reguläre Ausdruck zusammengestellt wird. Zu diesem Zweck .join(d.keys())könnte durch ersetzt werden .join(re.escape(key) for key in d.keys()).
Jochen
Bitte beachten Sie, dass das erste Beispiel (englосуг nicht englishA) nur in Python3 funktioniert. In Python2 gibt es mir immer noch "Спорт not englishA"
Fruit
Es scheint zu scheitern, wenn das Wort im Diktat einen Punkt hat - https://regex101.com/r/bliVUS/1- Ich muss es \bam Ende entfernen, bin mir aber nicht sicher, ob es korrekt ist.
Peter.k
24

Sie können die Reduktionsfunktion verwenden:

reduce(lambda x, y: x.replace(y, dict[y]), dict, s)
Codeape
quelle
16
Anders als bei der Lösung von @Max Shawabkeh werden reducebei der Verwendung die Substitutionen nacheinander angewendet . Infolgedessen { 'red': 'green', 'green': 'red'}funktioniert das Austauschen von Wörtern mithilfe von Wörterbüchern nicht mit dem reducebasierenden Ansatz, und überlappende Übereinstimmungen werden auf unvorhersehbare Weise transformiert.
Jochen
2
Ein gutes Beispiel dafür, warum wiederholte .replace()Anrufe unbeabsichtigte Folgen haben können: - html.replace('"', '"').replace('&', '&')Versuchen Sie es weiter html = '"foo"'.
Zigg
Dies ist unnötig komplex und unlesbar im Vergleich zu der entfalteten Schleife, wie in den Antworten von ChristopheD oder user2769207 gezeigt .
Poke
17

Hier gefundene Lösung (ich mag die Einfachheit):

def multipleReplace(text, wordDict):
    for key in wordDict:
        text = text.replace(key, wordDict[key])
    return text
ChristopheD
quelle
11
Wie @jochen beschrieben hat, riskiert dies wiederum eine schlechte Übersetzung, wenn es einen Schlüssel gibt, der auch ein Wert ist. Ein Single-Pass-Ersatz wäre am besten.
Chris
5

ein Weg, ohne re

d = {
'Спорт':'Досуг',
'russianA':'englishA'
}

s = 'Спорт russianA'.split()
for n,i in enumerate(s):
    if i in d:
        s[n]=d[i]
print ' '.join(s)
Ghostdog74
quelle
3
Dies wird fehlschlagen, wenn das Diktat Platz in seinen Schlüsseln hat
Trinh Hoang Nhu
3

Fast das gleiche wie Ghostdog74, obwohl unabhängig erstellt. Ein Unterschied besteht darin, dass die Verwendung von d.get () anstelle von d [] Elemente verarbeiten kann, die nicht im Diktat enthalten sind.

>>> d = {'a':'b', 'c':'d'}
>>> s = "a c x"
>>> foo = s.split()
>>> ret = []
>>> for item in foo:
...   ret.append(d.get(item,item)) # Try to get from dict, otherwise keep value
... 
>>> " ".join(ret)
'b d x'
Extraneon
quelle
1

Ich habe dies in einer ähnlichen Situation verwendet (meine Zeichenfolge war alle in Großbuchstaben):

def translate(string, wdict):
    for key in wdict:
        string = string.replace(key, wdict[key].lower())
    return string.upper()

hoffe das hilft irgendwie ... :)

user2769207
quelle
2
Es ist der Lösung von ChristopheD sehr ähnlich. Stimmen Sie ihm nicht zu?
Hynekcer
1

Mit der Warnung, dass es fehlschlägt, wenn der Schlüssel über Speicherplatz verfügt, handelt es sich um eine komprimierte Lösung, die der Antwort von ghostdog74 und extaneons ähnelt:

d = {
'Спорт':'Досуг',
'russianA':'englishA'
}

s = 'Спорт russianA'

' '.join(d.get(i,i) for i in s.split())
Anton vBR
quelle