Ах, спасибо, что включили структуру данных. Я думаю, что вам нужно "или" функциональность в регулярных выражениях. Рассмотрим этот пример
regex = r'Mr\. John Smith|John Smith'
re.findall(regex, "I hate Mr. John Smith)
# -> ['Mr. John Smith']
Итак, чтобы объяснить, канал в регулярном выражении действует как «или», то есть соответствует либо тому, либо другому, но не обоим, а регулярное выражение жадное будет соответствовать самому длинному шаблону, если между ними есть вложенность.
В приведенном мною примере и "Мистер Джон Смит", и "Джон Смит" были совпадением, но регулярное выражение выбрало совпадение с более длинным. Также обратите внимание, что findall () возвращает список всех совпадений. Итак, применив это к вашему случаю:
for k,v in eng_names_dict.items():
# Convert list of matches into one regex string
regex = r'|'.join(v)
matches = re.findall(regex, text)
namesDict[k] += len(matches)
EDIT
Хорошо, так что из вашего комментария кажется, что может быть неоднозначность в значениях различных ключей eng_names_dict, тогда как до сих пор Мой ответ касается только двусмысленности значений в одном ключе.
Вот два способа справиться с ситуацией и ограничения каждого из них. С помощью регулярных выражений иногда возникает неоднозначность, которая должна быть решена с помощью жестко закодированных правил.
Сценарий 1: небольшое количество таких неоднозначных случаев.
Если величина перекрытия между значениями мала и поддается управлению , вы могли бы упорядочить свои выражения регулярных выражений в соответствии с предпочтениями и по частям удалить соответствующую фразу в тексте.
Так, например, если у нас есть:
{'Mark Smith': ['Dr. Mark Smith', 'Mark Smith'],
'Andrew Mark': ['Dr. Mark', 'Andrew Mark']
Примечание. предполагая, что у Марка Смита есть где-то значение «доктор Марк Смит», даже если вы не говорите, что это обязательно так. Потому что, если это не так, то проблема в чем-то совершенно ином (в этом случае будет то, как сопоставить «Марка Смита», а НЕ как «доктора Марка Смита»).
Мы можем четко видим, что одно из значений Эндрю вложено в одно из значений Марка, поэтому мы можем сначала выбрать Марка (согласно некоторому правилу), а затем удалить фразу из текста.
from collections import OrderedDict
od = OrderedDict()
od['Mark Smith'] = eng_names_dict['Mark_Smith']
od['Andrew Mark'] = eng_names_dict['Andrew Mark']
for k,v in eng_names_dict.items():
# Convert list of matches into one regex string
regex = r'|'.join(v)
matches = re.findall(regex, text)
for match in set(matches):
text=re.sub(r'{}'.format(match, '', text)
namesDict[k] += len(matches)
Недостатком здесь является ручное требование для определения порядка операций при использовании записей eng_name_dicts.
Сценарий 2: количество дел слишком велико
В этом случае мы можем просто продолжать использовать естественное поведение регулярных выражений для выбора самой длинной строки в совпадениях с "или". Немного переделайте исходное решение. Вместо создания небольшого регулярного выражения для каждого ключа eng_names_dict, давайте создадим один действительно большой для каждого возможного значения. Регулярное решение решит для нам, каков правильный порядок.
# Create one list containing all values from dict
match_vals = []
for dict_val in list(eng_names_dict.values()):
for match_val in dict_val:
match_vals.extend(match_val)
# Do a match on this full regex
regex = r'|'.join(match_vals)
matches = re.findall(regex, text)
# Loop through every match, and count it if it's in the vals of an entry's key
for match in matches:
for k, v in eng_names_dict.items():
# Nested loops will be slow; open to suggestions to improve
if match in v:
namesDict[k] + 1
# Any match is unique to one person; break loop after match found
break
Преимущество в том, что регулярное выражение будет естественным образом определять наиболее точный порядок, так что вам не нужно это выяснять вручную. Недостатком здесь является то, что он сложен, труден для отладки и может повлиять на отношения между значениями имен, которые вы сами не знаете.