Может ли регулярное выражение Python отрицать список слов? - PullRequest
0 голосов
/ 30 ноября 2011

Я должен сопоставить все буквенно-цифровые слова из текста.

>>> import re
>>> text = "hello world!! how are you?"
>>> final_list = re.findall(r"[a-zA-Z0-9]+", text)
>>> final_list
['hello', 'world', 'how', 'are', 'you']
>>>

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

>>> negate_words = ['world', 'other', 'words']

Плохой способ сделать это

>>> negate_str = '|'.join(negate_words)
>>> filter(lambda x: not re.match(negate_str, x), final_list)
['hello', 'how', 'are', 'you']

Но я могу сохранить цикл, если мой самый первый шаблон регулярных выражений может быть изменен, чтобы учесть отрицание этих слов. Я обнаружил отрицание символов, но у меня есть слова для отрицания, также я нашел regex-lookbehind в других вопросах, но это тоже не помогает.

Можно ли это сделать с помощью python re?

Обновление

Мой текст может занимать несколько строк. Кроме того, список negate_words может быть слишком длинным.

Учитывая это, использует регулярное выражение для такой задачи, исправить в первую очередь ?? Любые предложения ??

Ответы [ 3 ]

6 голосов
/ 30 ноября 2011

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

>>> re.findall(r"\b(?:world|other|words)|([a-zA-Z0-9]+)\b", text)
['hello', '', 'how', 'are', 'you']

Почему бы не использовать вместо этого наборы Python. Они очень быстрые:

>>> list(set(final_list) - set(negate_words))
['hello', 'how', 'are', 'you']

Если порядок важен, см. Ответ от @glglgl ниже. Его версия понимания списка очень читабельна. Вот быстрый, но менее читаемый эквивалент с использованием itertools :

>>> negate_words_set = set(negate_words)
>>> list(itertools.ifilterfalse(negate_words_set.__contains__, final_list))
['hello', 'how', 'are', 'you']

Другой альтернативой является создание списка слов за один проход с использованием re.finditer :

>>> result = []
>>> negate_words_set = set(negate_words)
>>> result = []
>>> for mo in re.finditer(r"[a-zA-Z0-9]+", text):
    word = mo.group()
    if word not in negate_words_set:
         result.append(word)

>>> result
['hello', 'how', 'are', 'you']
1 голос
/ 30 ноября 2011

Возможно, стоит попытаться выполнить синтаксический анализ для этого:

>>> from pyparsing import *

>>> negate_words = ['world', 'other', 'words']
>>> parser = OneOrMore(Suppress(oneOf(negate_words)) ^ Word(alphanums)).ignore(CharsNotIn(alphanums))
>>> parser.parseString('hello world!! how are you?').asList()
['hello', 'how', 'are', 'you']

Обратите внимание, что oneOf(negate_words) должно быть до Word(alphanums), чтобы убедиться, что оно совпадает ранее.

Редактировать: только длявесело, я повторил упражнение, используя lepl (также интересная библиотека для разбора)

>>> from lepl import *

>>> negate_words = ['world', 'other', 'words']
>>> parser = OneOrMore(~Or(*negate_words) | Word(Letter() | Digit()) | ~Any())
>>> parser.parse('hello world!! how are you?')
['hello', 'how', 'are', 'you']
0 голосов
/ 30 ноября 2011

Не задавайте слишком много бесполезных регулярных выражений.
Вместо этого подумайте с генераторами.

import re

unwanted = ('world', 'other', 'words')

text = "hello world!! how are you?"

gen = (m.group() for m in re.finditer("[a-zA-Z0-9]+",text))
li = [ w for w in gen if w not in unwanted ]

И вместо li можно создать генератор, также

...