前瞻和非捕获正则表达式
我正在尝试匹配电子邮件地址中@符号前面的部分:
LOCAL_RE_NOTQUOTED = """
((
\w # alphanumeric and _
| [!#$%&'*+-/=?^_`{|}~] # special chars, but no dot at beginning
)
(
\w # alphanumeric and _
| [!#$%&'*+-/=?^_`{|}~] # special characters
| ([.](?![.])) # negative lookahead to avoid pairs of dots.
)*)
(?<!\.)(?:@) # no end with dot before @
"""
测试用:
re.match(LOCAL_RE_NOTQUOTED, "a.a..a@", re.VERBOSE).group()
结果是:
'a.a..a@'
为什么输出中会显示@
,尽管我使用了不捕获组(?:@)
呢?
测试用:
re.match(LOCAL_RE_NOTQUOTED, "a.a..a@", re.VERBOSE).groups()
结果是:
('a.a..a', 'a', 'a', None)
为什么这个正则表达式没有拒绝包含一对点'..'
的字符串呢?
1 个回答
13
你把不捕获组 (?:...)
和前瞻断言 (?=...)
搞混了。
不捕获组是参与匹配的,也就是说它们会出现在 match.group()
里面,这个包含了整体的匹配结果,只是它们不会生成可以后续使用的引用(比如 $1
之类的)。
第二个问题(为什么会匹配到两个点?)就有点复杂了。这是因为你的正则表达式有个错误。你看,当你写的时候(为了说明问题,简化了一下)
[+-/]
你写的是“匹配一个在 +
和 /
之间的字符,而在 ASCII 码中,点正好在它们之间(ASCII 43-47: +,-./
)。所以,第一个字符类匹配到了点,而前瞻断言根本没有被执行到。你需要把连字符放在字符类的最后面,这样它才会被当作一个普通的连字符来处理:
((
\w # alphanumeric and _
| [!#$%&'*+/=?^_`{|}~-] # special chars, but no dot at beginning
)
(
\w # alphanumeric and _
| [!#$%&'*+/=?^_`{|}~-] # special characters
| ([.](?![.])) # negative lookahead to avoid pairs of dots.
)*)
(?<!\.)(?=@) # no end with dot before @
当然,如果你想用这个逻辑,可以稍微简化一下:
^(?!\.) # no dot at the beginning
(?:
[\w!#$%&'*+/=?^_`{|}~-] # alnums or special characters except dot
| (\.(?![.@])) # or dot unless it's before a dot or @
)*
(?=@) # end before @