Самый простой способ заменить строку, используя словарь замен?

Рассматривать..

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

s = 'Спорт russianA'

Я хотел бы заменить все ключи DICT соответствующими значениями DICT в s,

9 ответов

Решение

Используя 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()))

Обратите внимание, что в этом случае вы должны отсортировать слова по убыванию по длине, если некоторые из ваших словарных статей являются подстроками других.

Вы можете использовать функцию Reduce:

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

Решение найдено здесь (мне нравится его простота):

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

В одну сторону, без ре

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

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

Почти так же, как 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'

Я использовал это в аналогичной ситуации (моя строка была в верхнем регистре):

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

надеюсь, что это поможет в некотором роде...:)

С предупреждением о сбое, если у ключа есть место, это сжатое решение, подобное ghostdog74, и ответы extaneons:

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

s = 'Спорт russianA'

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

Использование регулярного выражения

Мы можем создать регулярное выражение, которое соответствует любому из ключей словаря поиска, создав регулярные выражения для соответствия каждому отдельному ключу и объединив их с|. Мы используемre.subчтобы выполнить замену, предоставив ему функцию для выполнения замены (эта функция, конечно, будет выполнять поиск в словаре). Собираем вместе:

      import re

# assuming global `d` and `s` as in the question

# a function that does the dict lookup with the global `d`.
def lookup(match):
    return d[match.group()]

# Make the regex.
joined = '|'.join(re.escape(key) for key in d.keys())
pattern = re.compile(joined)

result = pattern.sub(lookup, s)

Здесь,re.escapeиспользуется для экранирования любых символов со специальным значением в заменах (чтобы они не мешали построению регулярного выражения и сопоставлялись буквально).

Этот шаблон регулярного выражения будет соответствовать подстрокам везде, где они появляются, даже если они являются частью слова или охватывают несколько слов. Чтобы избежать этого, измените регулярное выражение, чтобы оно проверяло границы слов:

      # pattern = re.compile(joined)
pattern = re.compile(rf'\b({joined})\b')

С использованиемstr.replaceитеративно

Просто переберите.items()поискового словаря и вызвать.replaceс каждым. Поскольку этот метод возвращает новую строку и не изменяет (не может) строку на месте , мы должны переназначить результаты внутри цикла:

      for to_replace, replacement in d.items():
    s = s.replace(to_replace, replacement)

Этот подход прост в написании и понимании, но он имеет несколько предостережений.

Во-первых, его недостаток в том, что он работает последовательно, в определенном порядке. То есть каждая замена может мешать другим заменам. Учитывать:

      s = 'one two'
s = s.replace('one', 'two')
s = s.replace('two', 'three')

Это произведет'three three', нет'two three', поскольку'two'из первой замены сам будет заменен на втором шаге. Обычно это нежелательно; однако в том редком случае, когда это должно работать таким образом, этот подход является единственно практичным.

Этот подход также не может быть легко исправлен для соблюдения границ слов, потому что он должен соответствовать буквальному тексту, а «граница слова» может быть отмечена несколькими различными способами — с помощью различных видов пробелов, а также без текста в начале и в конце слова . нить.

Наконец, имейте в виду, что a не является идеальной структурой данных для этого подхода. Если мы будем перебирать dict, то его способность выполнять поиск по ключу бесполезна; а в Python 3.5 и ниже порядок dicts не гарантируется (что усугубляет проблему последовательной замены). Вместо этого было бы лучше указать список кортежей для замен:

      d = [('Спорт', 'Досуг'), ('russianA', 'englishA')]
s = 'Спорт russianA'

for to_replace, replacement in d: # no more `.items()` call
    s = s.replace(to_replace, replacement)

По токенизации

Проблема становится намного проще, если строка сначала разрезается на части ( токенизируется ) таким образом, что все, что должно быть заменено, теперь точно соответствует ключу dict. Это позволило бы напрямую использовать поиск dict и обрабатывать всю строку за один раз, а также не создавать собственное регулярное выражение.

Предположим, что мы хотим сопоставить полные слова. Мы можем использовать более простое, жестко закодированное регулярное выражение, которое будет соответствовать пробелам и использует группу захвата; передав этоre.split, мы разделяем строку на разделы с пробелами и без пробелов. Таким образом:

      import re

tokenizer = re.compile('([ \t\n]+)')
tokenized = tokenizer.split(s)

Теперь просматриваем каждый из токенов в словаре: если он присутствует, его следует заменить соответствующим значением, а в противном случае его следует оставить в покое (эквивалентно замене самим собой). Словарь.getМетод естественно подходит для этой задачи. Наконец, мы соединяем части обратно . Таким образом:

      s = ''.join(d.get(token, token) for token in tokenized)

В более общем случае, например, если заменяемые строки могут содержать пробелы, потребуется другое правило токенизации. Однако обычно можно придумать правило токенизации, более простое, чем регулярное выражение из первого раздела (сопоставляющее все ключи методом перебора).

Особый случай: замена отдельных символов

Если все ключи dict состоят из одного символа (технически, кодовой точки Unicode), можно использовать более конкретные методы. См. Лучший способ заменить несколько символов в строке?для деталей.

немного лямбды...

      # list of tuples version
(mrep:=lambda s,l: s if not l else mrep(s.replace(*l.pop()), l))

# dictionary version
(lambda s,d: (mrep:=lambda s,l : s if not l else
    mrep(s.replace(*l.pop()), l))(s, list(d.items())))

# "universal" list/dict version
(lambda s,u: (mrep:=lambda s,l: s if not l else
    mrep(s.replace(*l.pop()), l))(s, u if type(u)==type(list())
       else list(u.items())))
Другие вопросы по тегам