前瞻性和非捕获正则表达式
问题内容:
我正在尝试将电子邮件地址的本地部分与@字符匹配:
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)
为什么正则表达式不拒绝带有一对点的字符串'..'
?
问题答案:
您会混淆非捕获组(?:...)
和超前断言(?=...)
。
前者确实参加了比赛(因此其中一部分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 @