Python正则表达式能否否定单词列表?
我需要从一段文字中找出所有的字母数字单词。
>>> 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']