Python re.sub()不会替换所有匹配项

时间:2018-12-15 08:15:46

标签: python regex string replace capturing-group

我正在使用Python 3,我有两个字符串:abbcabbabca。我想删除每个单个字符的重复出现。例如:

abbcabb应该给出c,而abca应该给出bc

我尝试了以下正则表达式(here):

(.)(.*?)\1

但是,它给第一个字符串输出错误。另外,当我尝试另一个(here)时:

(.)(.*?)*?\1

但是,这一次又给出了错误的输出。怎么了?


python代码是一个打印语句:

print(re.sub(r'(.)(.*?)\1', '\g<2>', s)) # s is the string

5 个答案:

答案 0 :(得分:3)

无需正则表达式即可解决问题,如下所示

>>>''.join([i for i in s1 if s1.count(i) == 1])
'bc'
>>>''.join([i for i in s if s.count(i) == 1])
'c'

答案 1 :(得分:2)

re.sub()不执行重叠替换。替换第一个比赛后,它将开始寻找比赛结束。因此,当您在

上执行替换时
abbcabb

它首先将abbca替换为bbc。然后它将bb替换为空字符串。它不会返回并在bbc中寻找另一个匹配项。

如果需要,您需要编写自己的循环。

while True:
    newS = re.sub(r'(.)(.*?)\1', r'\g<2>', s)
    if newS == s:
        break
    s = newS
print(newS)

DEMO

答案 2 :(得分:2)

正则表达式似乎不是理想的解决方案

  • 它们不处理重叠,因此需要循环(如this answer中的循环),并且会一遍又一遍地创建字符串(性能会降低)
  • 他们在这里太杀了,我们只需要数数字符

我喜欢this answer,但是在列表理解中反复使用count每次都会遍历所有元素。

无需正则表达式且没有O(n**2)复杂性,只需O(n)使用collections.Counter即可解决

  • 首先非常轻松快捷地计算字符串的字符
  • 然后使用我们刚刚创建的计数器过滤字符串以测试计数是否匹配。

像这样:

import collections

s = "abbcabb"

cnt = collections.Counter(s)

s = "".join([c for c in s if cnt[c]==1])

(作为奖励,您可以更改计数以保留具有2、3的字符,无论出现什么情况)

答案 3 :(得分:1)

编辑:基于评论交换-如果您仅关注字母计数的均等性,那么您不希望使用正则表达式,而是希望使用@jon的推荐方法。 (如果您不关心顺序,那么使用较长字符串的性能更高的方法可能会使用collections.Counter之类的东西。)


关于您要匹配的内容,我的最佳猜测是:“一个或多个字符-称为此子模式A-随后是一组不同的一个或多个字符-称为此子模式B-再次是子模式A”

您可以将+用作“一个或多个”的快捷方式(而不是一次指定,然后在其余匹配中使用*),但是无论哪种方式,您都需要获取子模式正确。让我们尝试一下:

>>> import re
>>> pattern = re.compile(r'(.+?)(.+?)\1')
>>> pattern.sub('\g<2>', 'abbcabbabca')
'bbcbaca'

嗯。那没用。为什么?因为第一个模式不是贪婪的,所以我们的“子模式A”只能匹配字符串中的第一个a-毕竟确实出现了。因此,如果我们使用贪婪的匹配,Python将会回溯,直到找到与子模式A一样长的模式,而该模式仍然允许A-B-A模式出现:

>>> pattern = re.compile(r'(.+)(.+?)\1')
>>> pattern.sub('\g<2>', 'abbcabbabca')
'cbc'

对我很好。

答案 4 :(得分:0)

网站对此进行了很好的解释,将鼠标悬停并使用“说明”部分。

(.)(.*?)\1不会删除或匹配所有重复出现的事件。它匹配1个字符,其后是中间的任何东西,直到再次遇到相同的字符为止。

因此,对于abbcabb,“三明治”部分应位于两个bbc之间的a

编辑: 您可以尝试不使用正则表达式的方法,如下所示:

string = "abbcabb"
result = []
for i in string:
    if i not in result:
        result.append(i)
    else:
        result.remove(i)
print(''.join(result))

请注意,这会产生字符串的“最后”奇数次出现,而不是首先出现。

对于“首次”已知发生,您应使用此answer中建议的计数器。只需更改条件以检查奇数。 pseudo code(count[letter] %2 == 1)