正则表达式绕过仅三个点

0 投票
2 回答
2696 浏览
提问于 2025-04-18 13:02

我有一个这样的字符串:

z='Certainly. I like apples... Really? By all means. Yes!'

我想要提取出 .?! 这几个符号,但不想要 ... 这个子串,所以我想要的结果应该是:

['.', '?', '.', '!']

这是我目前的代码:

>>> re.findall('(?<!\.)[\.?!]', z)
['.', '.', '?', '.', '!']

这个正则表达式也把 ... 这个子串里的第一个点给捕获了。但是如果我运行:

>>> re.findall('(?<!\.{2})[\.?!]', z)
['.', '.', '.', '?', '.', '!']

结果和我预期的完全相反,我不明白为什么,因为我已经要求前面有两个点的时候不匹配(尝试使用量词的任意操作符 +?*{1,2} 会出错,因为向后查找需要固定宽度的模式)。

我有些误解了,因为我本来以为在向前查找中加上第二个 \. 会得到我想要的结果。

如果有任何建议和简短的解释,我会非常感激(我找不到和我问的完全一样的内容)。

2 个回答

2

还有一种可能性:

pat = re.compile(r"\.\.\.|([.?!])")
matches = filter(None, pat.findall(z))

这个方法是通过匹配字面上的 ... 字符串来实现的。在我们有机会把它放进一个捕获组之前(在“或”符号 | 的另一边),就先把这个字符串消耗掉。然后再过滤结果,去掉所有的 ''(这是 findall 在找到一个空的捕获组时使用的)。

这个方法被一些人称为 最佳正则表达式技巧

2

你可以试试下面这个正则表达式,它使用了负向前瞻和负向后顾

>>> import re
>>> z='Certainly. I like apples... Really? By all means. Yes!'
>>> z
'Certainly. I like apples... Really? By all means. Yes!'
>>> m = re.findall(r'(?<!\.)[.?!](?!\.)', z)
>>> m
['.', '?', '.', '!']

示例

这个正则表达式会匹配那些前面没有点(.)并且后面也没有点的符号,比如点(.)、问号(?)或者感叹号(!)。

撰写回答