Совпадение и удаление повторяющихся символов: замена нескольких (3+) непоследовательных вхождений

9

Я ищу regexшаблон, который будет соответствовать третьему, четвертому, ... вхождению каждого персонажа. Посмотрите ниже для уточнения:

Например, у меня есть следующая строка:

111aabbccxccybbzaa1

Я хочу заменить все дублированные символы после второго вхождения. Выход будет:

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

Некоторые шаблоны регулярных выражений, которые я пробовал до сих пор:

Используя следующее регулярное выражение, я могу найти последнее вхождение каждого символа: (.)(?=.*\1)

Или используя этот, я могу сделать это для последовательных дубликатов, но не для любых дубликатов: ([a-zA-Z1-9])\1{2,}

M--
источник
1
Какой движок регулярных выражений вы планируете использовать с регулярным выражением?
Виктор Стрибьев
1
Вы можете сделать это только с регулярным выражением, поддерживающим бесконечную ширину, поэтому ваш единственный вариант - модуль регулярных выражений Python PyPi. Используйте это с (.)(?<=^(?:(?:(?!\1).)*\1){2,}(?:(?!\1).)*\1)регулярным выражением. Demo .
Виктор Стрибьев
3
@ WiktorStribiżew Это лучше чем (.)(?<=(.*\1){3})?
Стефан Покманн
2
@StefanPochmann Хорошо, (.)(?<=(?:.*\1){3})мы тоже сделаем эту работу, но все это не очень хорошо, так как чрезмерный возврат может вызвать проблемы с более длинными строками. Я бы предпочел написать метод без регулярных выражений, чтобы решить эту проблему.
Виктор Стрибьев
2
@ WiktorStribiżew Если я копирую СравниваемаяСтроку в regexstorm несколько раз, что делает его огромную строку, я получаю разницу в производительности , например , геометрические шаблоны 750ms, (.)(?<=(?:.*\1){3})25 мс, (.)(?<=(?:\1.*?){2}\1)3 мс. Вы можете просто проверить себя. Ваш, кажется, наименее эффективный шаблон, и его труднее читать.
пузырьковый пузырь

Ответы:

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было реализовано. Так что, если последний элемент xявляется 1, и это в четвертый раз 1произошло в xпоследний элемент rowid(x)является 4.

IceCreamToucan
источник
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.), затем разделяет массив от 2-го элемента до конца (удаляя первые два элемента, если они существуют)
  4. Задайте для строки s[:i]+'-'+s[i+1:]- конкатенировать подстроку до индекса с, -а затем подстроку после индекса, фактически пропуская исходный символ.
ctwheels
источник
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'
akrun
источник
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---"

Это перечисляет через строку, считая вхождения текущего символа позади него и только помещая символ, если это один из первых 2, иначе черточка.

ParkerD
источник
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---
CypherX
источник
0

Спасибо Виктору Стрибьеву , Стефану Похманну и Боббл-пузырю . Ради завершения я выкладываю возможно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---

Отрывок .

M--
источник