前瞻和非捕获正则表达式

5

我试图将电子邮件地址中@字符之前的本地部分与以下内容匹配:

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)

为什么正则表达式没有拒绝具有两个点 '..' 的字符串?

有时候,使用一个简单的正则表达式,例如/([\S]+)@/g,然后在下一步过滤输出中不需要的垃圾数据会更容易些。 - Jakub M.
@Jakub,是的,但这样就太容易了。 :) 不过,我想知道为什么它不起作用... - morfys
正则表达式中的#字符不应该被转义吗?(代码的第4行和第8行) - Jakub M.
@Jakub:不在字符类中。 - Tim Pietzcker
1个回答

12

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

前者确实参与匹配(因此是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 @

2
非常感谢您的回复。您在我的正则表达式中发现错误,真是太棒了! - morfys

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接