匹配和删除重复字符:替换多个(3个以上)不连续出现的相同字符

8
我希望您能提供一个正则表达式模式,以匹配每个字符的第三个、第四个...出现次数。请看下面的解释:
例如,我有以下字符串:
111aabbccxccybbzaa1

我想要替换第二次出现后的所有重复字符。输出结果如下:
11-aabbccx--y--z---

到目前为止,我尝试过的一些正则表达式模式:

使用以下正则表达式,我可以找到每个字符的最后一个出现:

(.)(?=.*\1)

或者使用这个正则表达式,可以找到连续重复的但不是任何重复的:

([a-zA-Z1-9])\1{2,}


1
你打算使用哪个正则表达式引擎来处理这个正则表达式? - Wiktor Stribiżew
1
请参见 https://rextester.com/ANLC41094,使用 pip install regex 安装 PyPi 正则表达式。 - Wiktor Stribiżew
3
比起 (.)(?<=(.*\1){3}),这个更好吗? - Stefan Pochmann
2
@StefanPochmann 嗯,(.)(?<=(?:.*\1){3}) 也可以完成任务,但所有这些方法都不好,因为过度回溯可能会导致长字符串出现问题。我宁愿编写一个非正则表达式的方法来解决这个问题。 - Wiktor Stribiżew
2
如果我将teststring复制到regexstorm中多次,使其成为一个巨大的字符串,就会出现性能差异,例如您的模式需要750毫秒,(.)(?<=(?:.*\1){3})只需要25毫秒,(.)(?<=(?:\1.*?){2}\1)只需要3毫秒。您可以自行测试。您的模式似乎是效率最低的,并且最难阅读。 - bobble bubble
显示剩余6条评论
6个回答

9
非正则表达式R解决方案。将字符串切分,替换此向量中的行ID >= 3*的元素为'-'。将其粘回一起。
x <- '111aabbccxccybbzaa1'

xsplit <- strsplit(x, '')[[1]]
xsplit[data.table::rowid(xsplit) >= 3] <- '-'
paste(xsplit, collapse = '')

# [1] "11-aabbccx--y--z---"

* rowid(x) 是一个整数向量,其每个元素表示从相应的x元素中实现该值的次数。因此,如果最后一个x元素的值为1,并且这是x中出现1的第四次,则rowid(x)的最后一个元素为4


5
您可以轻松地完成此操作而无需使用正则表达式: 在此处查看代码的使用情况
s = '111aabbccxccybbzaa1'

for u in set(s):
    for i in [i for i in range(len(s)) if s[i]==u][2:]:
        s = s[:i]+'-'+s[i+1:]

print(s)

结果:

11-aabbccx--y--z---

如何运作:

  1. for u in set(s) 获取字符串中唯一字符的列表:{'c','a','b','y','1','z','x'}
  2. for i in ... 循环遍历在步骤3中收集到的索引。
  3. [i for i in range(len(s)) if s[i]==u][2:] 循环遍历字符串中的每个字符,并检查它是否与步骤1中的 u 匹配,然后从第二个元素到结尾切片数组(如果存在,则删除前两个元素)。
  4. 将字符串设置为 s[:i]+'-'+s[i+1:] - 将子字符串连接到索引处和 - ,然后连接索引后的子字符串,有效地省略原始字符。

3
使用gsubfn的选项
library(gsubfn)
p <- proto(fun = function(this, x) if (count >=3) '-' else x)
for(i in c(0:9, letters)) x <- gsubfn(i, p, x)
x
#[1] "11-aabbccx--y--z---"

数据

x <- '111aabbccxccybbzaa1'

2

没有正则表达式的Python一行代码:

s = "111aabbccxccybbzaa1"

print("".join(char if s.count(char, 0, i) < 2 else "-" for i, char in enumerate(s)))
# ==> "11-aabbccx--y--z---"

这段代码通过枚举字符串,计算当前字符前面的出现次数,仅在当前字符为前两个字符之一时放置该字符,否则放置破折号。


1
另一种使用 pandas 的方法。
import pandas as pd

s = '111aabbccxccybbzaa1'
# 11-aabbccx--y--z---

df = pd.DataFrame({'Data': list(s)})
df['Count'] = 1
df['cumsum'] = df[['Data', 'Count']].groupby('Data').cumsum()
df.loc[df['cumsum']>=3, 'Data'] = '-'
''.join(df.Data.to_list())

Output:

11-aabbccx--y--z---

0

感谢Wiktor StribiżewStefan Pochmannbobble bubble。为了完整起见,我在此发布了评论中讨论的可能的regex解决方案;

只有支持无限宽度回溯后发的正则表达式才能实现这一点。使用Python PyPi regex模块,我们可以做到以下几点:

#python 2.7.12
import regex

s = "111aabbccxccybbzaa1"

print(regex.sub(r'(.)(?<=^(?:(?:(?!\1).)*\1){2,}(?:(?!\1).)*\1)', '-', s)) #Wiktor Stribizew
     ## 11-aabbccx--y--z---

print(regex.sub(r'(.)(?<=(.*\1){3})', '-', s)) #Stefan Pochmann
     ## 11-aabbccx--y--z---

print(regex.sub(r'(.)(?<=(?:.*\1){3})', '-', s)) #Wiktor Stribizew
     ## 11-aabbccx--y--z---

print(regex.sub(r'(.)(?<=(?:\1.*?){2}\1)', '-', s)) #bobble bubble
     ## 11-aabbccx--y--z---

查看代码实例


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