Python:正则表达式无法正常工作
Posted
技术标签:
【中文标题】Python:正则表达式无法正常工作【英文标题】:Python: Regular Expression not working properly 【发布时间】:2016-05-09 04:47:27 【问题描述】:我正在使用下面的正则表达式,它假设找到字符串'U.S.A.'
,但它只得到'A.'
,有人知道怎么了吗?
#INPUT
import re
text = 'That U.S.A. poster-print costs $12.40...'
print re.findall(r'([A-Z]\.)+', text)
#OUTPUT
['A.']
预期输出:
['U.S.A.']
我正在关注 NLTK 书,第 3.7 章 here,它有一组正则表达式,但它无法正常工作。我在 Python 2.7 和 3.4 都试过了。
>>> text = 'That U.S.A. poster-print costs $12.40...'
>>> pattern = r'''(?x) # set flag to allow verbose regexps
... ([A-Z]\.)+ # abbreviations, e.g. U.S.A.
... | \w+(-\w+)* # words with optional internal hyphens
... | \$?\d+(\.\d+)?%? # currency and percentages, e.g. $12.40, 82%
... | \.\.\. # ellipsis
... | [][.,;"'?():-_`] # these are separate tokens; includes ], [
... '''
>>> nltk.regexp_tokenize(text, pattern)
['That', 'U.S.A.', 'poster-print', 'costs', '$12.40', '...']
nltk.regexp_tokenize() 与 re.findall() 的工作方式相同,我认为我的 python 在这里无法按预期识别正则表达式。上面列出的正则表达式输出如下:
[('', '', ''),
('A.', '', ''),
('', '-print', ''),
('', '', ''),
('', '', '.40'),
('', '', '')]
【问题讨论】:
由于您没有提到模式并且如果您的唯一动机是使用(U.S.A.)
找到U.S.A.
就足够了。
见github.com/nltk/nltk/issues/1206和***.com/questions/32300437/…和***.com/questions/22175923/…
【参考方案1】:
这可能与之前使用 v3.1 中已废除的 nltk.internals.compile_regexp_to_noncapturing()
编译正则表达式的方式有关,请参阅 here)
>>> import nltk
>>> nltk.__version__
'3.0.5'
>>> pattern = r'''(?x) # set flag to allow verbose regexps
... ([A-Z]\.)+ # abbreviations, e.g. U.S.A.
... | \$?\d+(\.\d+)?%? # numbers, incl. currency and percentages
... | \w+([-']\w+)* # words w/ optional internal hyphens/apostrophe
... | [+/\-@&*] # special characters with meanings
... '''
>>>
>>> from nltk.tokenize.regexp import RegexpTokenizer
>>> tokeniser=RegexpTokenizer(pattern)
>>> line="My weight is about 68 kg, +/- 10 grams."
>>> tokeniser.tokenize(line)
['My', 'weight', 'is', 'about', '68', 'kg', '+', '/', '-', '10', 'grams']
但它在NLTK v3.1
中不起作用:
>>> import nltk
>>> nltk.__version__
'3.1'
>>> pattern = r'''(?x) # set flag to allow verbose regexps
... ([A-Z]\.)+ # abbreviations, e.g. U.S.A.
... | \$?\d+(\.\d+)?%? # numbers, incl. currency and percentages
... | \w+([-']\w+)* # words w/ optional internal hyphens/apostrophe
... | [+/\-@&*] # special characters with meanings
... '''
>>> from nltk.tokenize.regexp import RegexpTokenizer
>>> tokeniser=RegexpTokenizer(pattern)
>>> line="My weight is about 68 kg, +/- 10 grams."
>>> tokeniser.tokenize(line)
[('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', '')]
通过对定义正则表达式组的方式稍作修改,您可以使用此正则表达式在 NLTK v3.1 中获得相同的模式:
pattern = r"""(?x) # set flag to allow verbose regexps
(?:[A-Z]\.)+ # abbreviations, e.g. U.S.A.
|\d+(?:\.\d+)?%? # numbers, incl. currency and percentages
|\w+(?:[-']\w+)* # words w/ optional internal hyphens/apostrophe
|(?:[+/\-@&*]) # special characters with meanings
"""
在代码中:
>>> import nltk
>>> nltk.__version__
'3.1'
>>> pattern = r"""
... (?x) # set flag to allow verbose regexps
... (?:[A-Z]\.)+ # abbreviations, e.g. U.S.A.
... |\d+(?:\.\d+)?%? # numbers, incl. currency and percentages
... |\w+(?:[-']\w+)* # words w/ optional internal hyphens/apostrophe
... |(?:[+/\-@&*]) # special characters with meanings
... """
>>> from nltk.tokenize.regexp import RegexpTokenizer
>>> tokeniser=RegexpTokenizer(pattern)
>>> line="My weight is about 68 kg, +/- 10 grams."
>>> tokeniser.tokenize(line)
['My', 'weight', 'is', 'about', '68', 'kg', '+', '/', '-', '10', 'grams']
在没有 NLTK 的情况下,使用 python 的 re
模块,我们看到原生不支持旧的正则表达式模式:
>>> pattern1 = r"""(?x) # set flag to allow verbose regexps
... ([A-Z]\.)+ # abbreviations, e.g. U.S.A.
... |\$?\d+(\.\d+)?%? # numbers, incl. currency and percentages
... |\w+([-']\w+)* # words w/ optional internal hyphens/apostrophe
... |[+/\-@&*] # special characters with meanings
... |\S\w* # any sequence of word characters#
... """
>>> text="My weight is about 68 kg, +/- 10 grams."
>>> re.findall(pattern1, text)
[('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', '')]
>>> pattern2 = r"""(?x) # set flag to allow verbose regexps
... (?:[A-Z]\.)+ # abbreviations, e.g. U.S.A.
... |\d+(?:\.\d+)?%? # numbers, incl. currency and percentages
... |\w+(?:[-']\w+)* # words w/ optional internal hyphens/apostrophe
... |(?:[+/\-@&*]) # special characters with meanings
... """
>>> text="My weight is about 68 kg, +/- 10 grams."
>>> re.findall(pattern2, text)
['My', 'weight', 'is', 'about', '68', 'kg', '+', '/', '-', '10', 'grams']
注意: NLTK 的 RegexpTokenizer 编译正则表达式的方式的变化也会使 NLTK's Regular Expression Tokenizer 上的示例过时。
【讨论】:
【参考方案2】:删除尾随+
,或将其放入组中:
>>> text = 'That U.S.A. poster-print costs $12.40...'
>>> re.findall(r'([A-Z]\.)+', text)
['A.'] # wrong
>>> re.findall(r'([A-Z]\.)', text)
['U.', 'S.', 'A.'] # without '+'
>>> re.findall(r'((?:[A-Z]\.)+)', text)
['U.S.A.'] # with '+' inside the group
【讨论】:
【参考方案3】:正则表达式匹配的文本的第一部分是“U.S.A.”因为([A-Z]\.)+
匹配第一组(括号内的部分)三次。但是,每个组只能返回一个匹配项,因此 Python 会选择该组的最后一个匹配项。
如果您改为更改正则表达式以在组中包含“+”,则该组将只匹配一次并返回完整匹配。例如(([A-Z]\.)+)
或((?:[A-Z]\.)+)
。
如果您想要三个单独的结果,那么只需去掉正则表达式中的“+”号,它每次只会匹配一个字母和一个点。
【讨论】:
【参考方案4】:问题在于“捕获组”,也就是括号,它对findall()
的结果产生了意想不到的影响:当一个捕获组在比赛中被多次使用时,正则表达式引擎会丢失轨道并发生奇怪的事情.具体来说:正则表达式正确匹配整个U.S.A.
,但findall
将其丢弃在地板上,仅返回最后一组捕获。
正如this answer 所说,re
模块不支持重复捕获组,但您可以安装替代的regexp 模块来正确处理此问题。 (但是,如果您想将正则表达式传递给 nltk.tokenize.regexp
,这对您没有帮助。)
无论如何要正确匹配U.S.A.
,请使用:r'(?:[A-Z]\.)+', text)
。
>>> re.findall(r'(?:[A-Z]\.)+', text)
['U.S.A.']
您可以对 NLTK 正则表达式中的所有重复模式应用相同的修复,一切都会正常工作。正如@alvas 建议的那样,NLTK 曾经在幕后进行这种替换,但最近在标记器的文档中删除了这个功能并用a warning 替换。这本书显然已经过时了; @alvas 在 11 月就此事提交了 bug report,但尚未采取行动……
【讨论】:
以上是关于Python:正则表达式无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章