Python正则表达式:如何实现这个复杂的替换规则?

Python regex: how to achieve this complex replacement rule?

我正在处理长字符串,我需要用 '' 替换相邻句号 . and/or 冒号 : 的所有组合,但仅当它们不与任何空白相邻。示例:

好吧,这是我尝试过的方法(没有任何成功)。

尝试 1:

s1 = r'a.b.c.d.e.f.g.h'
re.sub(re.search(r'[^\s.:]+([.:]+)[^\s.:]+', s1).group(1), r'', s1)

我希望得到 'abcdefgh',但我实际得到的是 r''。我明白为什么:代码

re.search(r'[^\s.:]+([.:]+)[^\s.:]+', s1).group(1)

returns '.' 而不是 '\.',因此 re.search 不明白它必须替换单个句号 . 而不是将 '.' 理解为通常的正则表达式。

尝试 2:

s1 = r'a.b.c.d.e.f.g.h'
re.sub(r'([^\s.:]*\S)[.:]+(\S[^\s.:]*)', r'\g<1>\g<2>', s1)

这不起作用 returns a.b.c.d.e.f.gh

尝试 3:

s1 = r'a.b.c.d.e.f.g.h'
re.sub(r'([^\s.:]*)[.:]+([^\s.:]*)', r'\g<1>\g<2>', s1)

这适用于 s1,但它不能解决我的问题,因为在 s2 = r'a .b' 它 returns a b 而不是 a .b

有什么建议吗?

这里有多个问题。您的正则表达式与您想要匹配的内容不匹配;而且,你对 re.subre.search 的理解是错误的。

要查找内容,re.search 可让您查找字符串中出现内容的位置。

替换那个东西,在同一个正则表达式上使用re.sub 而不是 re.search,不是还有。

并且,了解 re.sub(r'thing(moo)other', '', s1) 整个匹配项 替换为替换字符串。

除此之外,对于您的正则表达式,听起来您想要

r'(?<![\s.:])[.:]+(?![\s.:])'   # updated from comments, thanks!

包含一个带有句号和冒号的字符 class(注意方括号内不需要反斜杠——这是一个点和冒号没有任何特殊含义的上下文1),尽可能重复多次;和环顾四周说我们不能匹配这些字符当有空格 \s 在任何一方,并且还排除字符本身,以便正则表达式引擎无法通过应用 [= 来找到匹配项20=] 不太严格(如果有办法,它会尽最大努力找到匹配项)。

现在,正则表达式只匹配您要实际替换的部分,因此您可以这样做

>>> import re
>>> s1 = 'name.surname@domain.com'
>>> re.sub(r'(?<![\s.:])[.:]+(?![\s.:])', r'', s1)
'namesurname@domaincom'

虽然在更广泛的方案中,您还需要知道如何保留比赛的某些部分。出于本次演示的目的,我将使用一个正则表达式,将点或冒号前后的文本捕获到带括号的组中:

>>> re.sub(r'(.*\S)[.:]+(\S.*)', r'\g<1>\g<2>', s1)
'name.surname@domaincom'

查看替换字符串中的 \g<1> 如何引用回 "whatever the first set of parentheses matched" 以及类似地 \g<2> 引用第二个带括号的组。

您还会注意到,这未能替换第一个句号,因为第一组括号内的 .* 匹配了尽可能多的字符串。为避免这种情况,您需要一个只匹配尽可能少的正则表达式。我们已经用环顾四周解决了上面的问题,所以我会把你留在这里,尽管以不同的方式解决这个问题会很有趣(但也不太难)。


1 你甚至可以说普通的正则表达式语言(或语法、符号或形式主义)与语言(或语法、符号或形式主义)是分开的方括号内!