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

Python re.sub() is not replacing every match

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

abbcabb 应该给出 cabca 应该给出 bc.

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

(.)(.*?)

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

(.)(.*?)*?

但是,这个又给出了错误的输出。这里出了什么问题?


python代码是打印语句:

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

网站解释得很好,悬停并使用解释部分。

(.)(.*?) 不删除或匹配每一次重复出现。它匹配 1 个字符,然后是夹在中间的任何字符,直到再次遇到相同的字符。

所以,对于 abbcabb,"sandwiched" 部分应该是 bbc 在两个 a

之间

编辑: 您可以在没有正则表达式的情况下尝试这样的事情:

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

请注意,这会产生 "last" 个字符串的奇数出现,而不是第一个。

对于 "first" 已知事件,您应该使用此 中建议的计数器。只需更改条件以检查奇数。 pseudo code(count[letter] %2 == 1)

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

上执行替换时
abbcabb

首先将abbca替换为bbc。然后它将 bb 替换为空字符串。它不会返回并在 bbc.

中寻找另一场比赛

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

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

DEMO

编辑:基于评论交换 - 如果您只关心字母计数的奇偶校验,那么您 不需要正则表达式 而是想要像这样的方法@jon 的推荐。 (如果您不关心顺序,那么使用非常长的字符串的更高效的方法可能会使用类似 collections.Counter 的方法。)


我对您要匹配的内容的最佳猜测是:"one or more characters - call this subpattern A - followed by a different set of one or more characters - call this subpattern B - followed by subpattern A again"。

您可以使用 + 作为 "one or more" 的快捷方式(而不是指定一次然后使用 * 进行其余匹配),但无论哪种方式,您都需要获得正确的子模式。让我们试试:

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

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

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

我觉得不错。

不用正则表达式也能解决,如下

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

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

  • 它们不处理重叠,所以它需要一个循环(就像在 中一样)并且它一遍又一遍地创建字符串(性能受到影响)
  • 他们这里有点过分了,我们只需要数一下字数就可以了

我喜欢 ,但是在列表理解中重复使用 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 次的字符)