Python: re.findall () не работает для перекрывающихся подстрок - PullRequest
0 голосов
/ 16 февраля 2019

Я хочу сопоставить строку со списком значений.Они могут перекрываться, например, string = "test1 test2" и values = ["test1", "test1 test2"].

РЕДАКТИРОВАТЬ: Ниже приведен весь мой код для простого примера

import regex    

string = "This is a test string"
values = ["test", "word", "string", "test string"]

pattern = r'\b({})\b'.format('|'.join(map(regex.escape, values)))
matches = set(map(str.lower, regex.findall(pattern, string, regex.IGNORECASE)))

output = ([x.upper() for x in values if x.lower() in matches])

print(output) # ['TEST', 'STRING']
# Expected output:  ['TEST', 'STRING', 'TEST STRING']

Ответы [ 2 ]

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

Если вы определили, что foobar находится в тексте, вам не нужно искать в тексте отдельно для foo и bar: вы уже знаете ответ.

Сначала сгруппируйте свои поиски:

searches = ['test', 'word', 'string', 'test string', 'wo', 'wordy']
unique = set(searches)
ordered = sorted(unique, key = len)
grouped = {}

while unique:
    s1 = ordered.pop()
    if s1 in unique:
        unique.remove(s1)
        grouped[s1] = [s1]
        redundant = [s2 for s2 in unique if s2 in s1]
        for s2 in redundant:
            unique.remove(s2)
            grouped[s1].append(s2)

for s, dups in grouped.items():
    print(s, dups)

# Output:
# test string ['test string', 'string', 'test']
# wordy ['wordy', 'word', 'wo']

После того, как вы сгруппировали вещи, вы можете ограничить поиск только поисками верхнего уровня (клавиши grouped).

Кроме того, если масштаб и производительностьпроблемы, вам действительно нужны регулярные выражения?Ваши текущие примеры могут быть обработаны обычными in тестами, которые быстрее.Если вам действительно нужны регулярные выражения, идея сгруппировать поиски сложнее, но, возможно, не исключена при некоторых условиях.

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

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

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

Другойразница, которую я заметил, между вашей установкой Python и моей - import regex.Видимо, вы используете более старую версию Python, как я import re (версия 3.7).Я проверял даже Python версии 2.7.15, он также использует import re.

Сценарий может выглядеть следующим образом:

import re

def mtch(pat, str):
    s = pat.search(str)
    return s.group().upper() if s else None

# Strings to look for
values = ["test", "word", "string", "test string"]
# Compile patterns
patterns = [ re.compile(r'\b({})\b'.format(re.escape(v)),
    re.IGNORECASE) for v in values ]
# The string to check
string = "This is a test string"
# What has been found
list(filter(None, [ mtch(pat, string) for pat in patterns ]))

mtch Функция возвращаеттекст, найденный с помощью pat (скомпилированный шаблон) в str (исходная строка) или Нет в совпадении не удалось.

patterns содержит список скомпилированных шаблонов.

Затем есть [ mtch(pat, string) for pat in patterns ] понимание списка, генерирующее список результатов совпадения (с Нет значениями, если попытка совпадения не удалась).

Для фильтрации Нет значения, которые я использовал filter функция.

И, наконец, list собирает все отфильтрованные строки и печатает:

['TEST', 'STRING', 'TEST STRING']

Если вы хотите выполнить этот поиск для множественный исходные строки, запустите только последний оператор для каждой исходной строки, возможно, добавив результат (и некоторое указание на то, какая строка была найдена) в некоторый список результатов.

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

Редактировать относительно комментария по состоянию на 2019-02-18 10: 00Z

Как ячитайте из вашего комментария, строки для чтения кода выглядят следующим образом:

with open("Test_data.csv") as f:
    for entry in f:
        entry = entry.split(',')
        string = entry[2] + " " + entry[3] + " " + entry[6] 

Обратите внимание, что вы перезаписываете string в каждом цикле, поэтому после завершения цикла у вас есть результат из last строка (только).

Или, может быть, сразу после прочтения вы запускаете поиск шаблонов для текущей строки?

Еще один совет по изменению кода:

  1. Избегайте таких комбинаций, что, например, переменная entry изначально содержит всю строку , а затем список - произведение разбиения.Возможно, более читаемый вариант:

    for row in f:
        entry = row.split(',')
    
  2. После того, как вы прочитали строку и, прежде чем делать что-либо еще, убедитесь, что прочитанная строка не пуста .Если строка пуста, опустите ее.Быстрый способ проверить это - просто использовать строку в if (пустая строка оценивается как False ).

    for row in f:
        if row:
            entry = row.split(',')
            ...
    
  3. До string = entry[2] + " " + entry[3] + " " + entry[6]проверьте, есть ли в списке entry хотя бы 7 пунктов (нумерация от 0).Может быть, некоторые из ваших входных строк содержат меньшее количество фрагментов, и, следовательно, ваша программа пытается прочитать из несуществующего элемента этого списка?

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

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