Найпростіший спосіб замінити рядок за допомогою словника замін?


74

Поміркуйте ..

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

s = 'Спорт russianA'

Я хотів би замінити всі ключі dict на відповідні значення dict в s.


1
Це може бути не так просто. Напевно, у вас повинен бути явний токенізатор (наприклад, {'cat': 'russiancat'}"гусениця"). Також перекриваються слова ( {'car':'russiancar', 'pet' : 'russianpet'}і "килим").
Джо


2
Окрім того: я вважаю, що dictйого краще уникати як ім’я змінної, оскільки змінна цього імені затьмарює вбудовану функцію того самого імені.
jochen

Відповіді:


98

Використання 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'

Це буде відповідати лише цілим словам. Якщо вам це не потрібно, використовуйте шаблон:

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

Зверніть увагу, що в цьому випадку слід сортувати слова за спаданням за довжиною, якщо деякі з ваших словникових статей є підрядками інших.


24
Якщо ключі словника містять такі символи, як "^", "$" та "/", перед збиранням регулярного виразу потрібно клавіші захистити. Для цього .join(d.keys())можна замінити на .join(re.escape(key) for key in d.keys()).
jochen

Зверніть увагу, що перший приклад (Досуг, а не englishA) працює лише в python3. У python2 мені все ще повертається "Спорт не англійськаA"
Фрукти

Здається, це не вдається, коли слово в dict має крапку - https://regex101.com/r/bliVUS/1- Мені потрібно видалити \bв кінці, але не впевнений, що це правильно.
Peter.k

25

Ви можете використовувати функцію зменшення :

reduce(lambda x, y: x.replace(y, dict[y]), dict, s)

16
На відміну від рішення @Max Shawabkeh, використання reduceзастосовує заміни одна за одною. Як наслідок, обмін словами за допомогою словників { 'red': 'green', 'green': 'red'}не працює з reduceпідходом на основі, а збіги, що перекриваються, трансформуються непередбачувано.
jochen

2
Хороший приклад того, чому повторні .replace()дзвінки можуть мати непередбачені наслідки: - html.replace('"', '"').replace('&', '&')спробуйте html = '"foo"'.
zigg

Це надмірно складне та нечитабельне порівняно з розгорнутим циклом, як показано у відповідях ChristopheD або user2769207 .
тикати

17

Рішення знайдено тут (мені подобається його простота):

def multipleReplace(text, wordDict):
    for key in wordDict:
        text = text.replace(key, wordDict[key])
    return text

11
Знову ж таки, як описав @jochen, це ризикує погано перекласти, якщо є ключ, який також є цінністю. Однопрохідна заміна буде найкращою.
Кріс

5

в один бік, без ре

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

s = 'Спорт russianA'.split()
for n,i in enumerate(s):
    if i in d:
        s[n]=d[i]
print ' '.join(s)

3
Це не вдасться, якщо в ключі є місце в ключах
Трін Хоанг Ню

3

Майже те саме, що ghostdog74, хоча створений самостійно. Одна відмінність - використання d.get () замість d [] може обробляти елементи, що не містяться в дикті.

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

1

Я використовував це у подібній ситуації (мій рядок був у верхньому регістрі):

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

сподіваюся, що якимось чином допомагає ... :)


2
Це дуже схоже на рішення ChristopheD. Ви з ним не згодні?
hynekcer

1

З попередженням про те, що він не працює, якщо в ключі є простір, це стиснене рішення, подібне до ghostdog74 і extaneons відповідає:

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

s = 'Спорт russianA'

' '.join(d.get(i,i) for i in s.split())
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.