Python: сопоставить несколько подстрок в строке - PullRequest
0 голосов
/ 01 февраля 2019

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

str = "This is a test string from which I want to match multiple substrings"
value = ["test", "match", "multiple", "ring"]
temp = []
temp.extend([x.upper() for x in value if x.lower() in str.lower()])
print(temp)

, что приводит к temp = ["TEST", "MATCH", "MULTIPLE", "RING"]

Тем не менее, это не тот результат, который я хотел бы.Подстроки должны иметь точное совпадение, поэтому «кольцо» не должно совпадать с «строкой».

Вот почему я пытался решить эту проблему с помощью регулярных выражений, например:

str = "This is a test string from which I want to match multiple substrings"
value = ["test", "match", "multiple", "ring"]
temp = []
temp.extend([x.upper() for x in value if regex.search(r"\b" + regex.escape(x) + r"\b", str,
                                                   regex.IGNORECASE) is not None])
print(temp)

что приводит к ["TEST", "MATCH", "MULTIPLE"], правильному решению.Как бы то ни было, это решение занимает слишком много времени для вычисления.Я должен выполнить эту проверку для примерно 1 миллиона строк, и решение с использованием регулярных выражений займет несколько дней, по сравнению с 1,5 часами, которые требуются при использовании первого решения.

Я хотел бы знать, есть ли способ заставить первое решение работать или второе решение работать быстрее.Заранее спасибо

РЕДАКТИРОВАТЬ: value также может содержать цифры или короткие фразы, такие как "test1 test2"

Ответы [ 3 ]

0 голосов
/ 01 февраля 2019

Трудно предложить оптимальное решение, не видя фактических данных, но вы можете попробовать следующее:

  • Создание единого шаблона, соответствующего всем значениям.Таким образом, вам нужно будет искать строку только один раз (вместо одного значения).
  • Пропускать экранирующие значения, если они не содержат специальных символов (например, '^' или '*').
  • Присвойте результат непосредственно temp, избегая ненужного копирования с помощью temp.extend().
import regex

# 'str' is a built-in name, so use 'string' instead
string = 'This is a Test string from which I want to match multiple substrings'
values = ['test', 'test2', 'Multiple', 'ring', 'match']
pattern = r'\b({})\b'.format('|'.join(map(regex.escape, values)))

# unique matches, lowercased
matches = set(map(str.lower, regex.findall(pattern, string, regex.IGNORECASE)))

# arrange the results as they appear in `values`
temp = [x.upper() for x in values if x.lower() in matches]
print(temp)  # ['TEST', 'MULTIPLE', 'MATCH']
0 голосов
/ 01 февраля 2019

На ум приходят две возможные оптимизации:

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

import re

str = "This is a test string from which I want to match test1 test2 multiple substrings"
values = ["test", "match", "multiple", "ring", "test1 test2"]

pattern = re.compile("|".join(r"\b" + re.escape(x) + r"\b" for x in values))
temp = []

temp.extend([x.upper() for x in pattern.findall(str, re.IGNORECASE)])
print(temp)

Результат:

['TEST', 'MATCH', 'TEST1 TEST2', 'MULTIPLE']

Потенциальные недостатки этого подхода:

  • Возможно, выходные данные будут в другом порядке.Ваш оригинальный подход размещает результаты в порядке их появления в values.При таком подходе результаты располагаются в порядке их появления в str.
  • , то же самое значение будет появляться несколько раз в temp, если оно появлялось несколько раз в str.В отличие от вашего первоначального подхода, когда значение появляется не более одного раза в temp.
  • search, оно заканчивается, как только находит совпадение.findall всегда ищет всю строку.Если вы ожидаете, что большинство ваших строк соответствуют каждому слову в value, и ожидаете, что большинство совпадений появятся в начале строки, то findall может быть медленнее, чем search.С другой стороны, если вы ожидаете, что поиск будет часто показывать None, то findall, вероятно, будет несколько быстрее.
0 голосов
/ 01 февраля 2019

Вы можете разделить str на пробелы, а затем сопоставить элементы из value с ==

РЕДАКТИРОВАТЬ:

Итак, вы сказали, что некоторые строки в values могутесть место до или после них.Вы можете решить это с помощью следующей строки:

values = [i.strip() for i in values]

Это удалит все пробельные символы до и после строки (в вашем случае для каждого элемента).

Кроме того, вы упомянули, чтоесли вы разделите str на пробел, некоторые слова будут иметь знаки препинания, оставшиеся после разделения -> 'Hi, how are you?' приведет к ['Hi,', 'how', 'are', 'you?'].Вы можете решить эту проблему, используя встроенный метод startswith() для фильтрации всех слов, начинающихся с элементов из values, например:

str = ['Hi,', 'how', 'are', 'you?']`
values = ['how', 'you', 'time', 'space']

new_str = []
for word in str:
  for j in values:
    if word.startswith(j):
      new_str.append(word)

# result -> ['how', 'you?']

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

Надеюсь, теперь все стало понятнее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...