匹配和删除重复的字符:替换多个(3+)非连续出现的字符


9

我正在寻找一个regex与每个字符的第三,第四,...匹配的模式。请看下面的说明:

例如,我有以下字符串:

111aabbccxccybbzaa1

我想在第二次出现后替换所有重复的字符。输出将是:

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

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

使用以下正则表达式,我可以找到每个字符的最后一次出现: (.)(?=.*\1)

或使用此方法,我可以将其用于连续重复,但不能重复: ([a-zA-Z1-9])\1{2,}


1
您打算将哪种正则表达式引擎与正则表达式一起使用?
WiktorStribiżew19年

1
您只能使用支持无限宽后向的正则表达式来执行此操作,因此,您唯一的选择是Python PyPi正则表达式模块。与(.)(?<=^(?:(?:(?!\1).)*\1){2,}(?:(?!\1).)*\1)正则表达式一起使用。演示
维克多·斯特里比维(WiktorStribiżew)

3
@WiktorStribiżew比那更好(.)(?<=(.*\1){3})吗?
Stefan Pochmann

2
@StefanPochmann很好,(.)(?<=(?:.*\1){3})也可以做,但是所有这些都不是很好,因为过多的回溯可能会导致字符串较长的问题。我宁愿写一个非正则表达式的方法来解决这个问题。
WiktorStribiżew19年

2
@WiktorStribiżew如果我多次将测试字符串复制到regexstorm中,使其成为一个巨大的字符串,我会得到性能差异,例如您的模式750ms,(.)(?<=(?:.*\1){3})25ms,(.)(?<=(?:\1.*?){2}\1)3ms。您可以测试自己。您的模式似乎是效率最低的模式,最难阅读。
泡泡泡泡

Answers:


8

非正则表达式R解决方案。分割字串。用替换此向量的rowid> = 3 *的元素'-'。粘贴回去。

x <- '111aabbccxccybbzaa1'

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

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

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


4

您无需使用正则表达式即可轻松完成此操作:

在这里查看正在使用的代码

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:]循环遍历字符串中的每个字符并检查是否匹配u(从步骤1开始),然后将数组从第二个元素切到结尾(如果存在则删除前两个元素)
  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

没有regex 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())

输出

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

0

感谢WiktorStribiżewStefan Pochmann泡泡龙。为了完整起见,我将发布regex评论中讨论的可能解决方案;

这仅适用于支持无限宽后向查找的正则表达式。使用Python PyPi正则表达式模块,我们可以执行以下操作:

#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---

片段

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.