前瞻性和非捕获正则表达式

mor*_*fys 3 python regex regex-lookarounds

我想在@字符之前匹配电子邮件地址的本地部分:

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 @
"""
Run Code Online (Sandbox Code Playgroud)

测试:

re.match(LOCAL_RE_NOTQUOTED, "a.a..a@", re.VERBOSE).group()
Run Code Online (Sandbox Code Playgroud)

得到:

'a.a..a@'
Run Code Online (Sandbox Code Playgroud)

为什么@打印在输出中,即使我使用的是非捕获组(?:@)

测试:

 re.match(LOCAL_RE_NOTQUOTED, "a.a..a@", re.VERBOSE).groups()
Run Code Online (Sandbox Code Playgroud)

得到:

('a.a..a', 'a', 'a', None)
Run Code Online (Sandbox Code Playgroud)

为什么正则表达式不会拒绝带有一对点的字符串'..'

Tim*_*ker 6

你会混淆非捕获组(?:...)和前瞻性断言(?=...).

前者确实参与了比赛(因此其中一部分match.group()包含整体匹配),他们只是不生成反向引用($1等以供以后使用).

第二个问题(为什么双点匹配?)有点棘手.这是因为正则表达式中存在错误.你看,你写的时候(缩短为要点)

[+-/]
Run Code Online (Sandbox Code Playgroud)

你写了"在+和之间匹配一个字符/,并且在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 @
Run Code Online (Sandbox Code Playgroud)

当然,如果你想使用这个逻辑,你可以简化一下:

^(?!\.)                   # no dot at the beginning
(?:
[\w!#$%&'*+/=?^_`{|}~-]   # alnums or special characters except dot
| (\.(?![.@]))            # or dot unless it's before a dot or @ 
)*
(?=@)                     # end before @
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢你的回复。你在我的正则表达式中发现错误真是太不可思议了...... (2认同)