Python正则表达式能否否定单词列表?

0 投票
3 回答
2969 浏览
提问于 2025-04-17 07:22

我需要从一段文字中找出所有的字母数字单词。

>>> 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']

不过,如果我能在一开始就把正则表达式改成可以排除那些单词的样子,就能省去一个循环。我找到了一些关于排除字符的方法,但我需要排除的是单词。另外,我在其他问题中看到过正则表达式的“向后查找”,但那也没什么帮助。

用Python的re模块能做到这一点吗?

更新

我的文本可能有几百行。而且,排除的单词列表也可能很长。

考虑到这一点,用正则表达式来做这个任务,真的合适吗? 有什么建议吗?

3 个回答

-1

别对正则表达式要求太多,没必要。
相反,可以考虑使用生成器。

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

1

也许可以试试pyparsing来解决这个问题:

>>> 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']
6

我觉得用正则表达式来实现这个功能并不是一个很好的方法。虽然我找到的最接近的解决方案有点复杂,而且不完全符合你的需求:

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

不如用Python的集合(sets)来做,这样会更快:

>>> 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']

撰写回答