Збіг та видалення дублюваних символів: Замініть декілька (3+) непослідовних подій


9

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

Наприклад, у мене є такий рядок:

111aabbccxccybbzaa1

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

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

Деякі зразки регексу, які я спробував поки що:

Використовуючи наступний вираз, я можу знайти останнє виникнення кожного символу: (.)(?=.*\1)

Або використовуючи цей, я можу це зробити для послідовних дублікатів, але не для дублікатів: ([a-zA-Z1-9])\1{2,}


1
Який двигун regex ви плануєте використовувати разом з регулярним виразом?
Wiktor Stribiżew

1
Ви можете це зробити лише за допомогою регулярного виразу, який підтримує нескінченну ширину ззаду, тож ваш єдиний варіант - це модуль для регулярного вирівнювання Python PyPi. Використовуйте його з (.)(?<=^(?:(?:(?!\1).)*\1){2,}(?:(?!\1).)*\1)регулярним виразом. Демо .
Wiktor Stribiżew

3
@ WiktorStribiżew Це краще, ніж (.)(?<=(.*\1){3})?
Стефан Похман

2
@StefanPochmann Добре також (.)(?<=(?:.*\1){3})буде виконувати цю роботу, але все це не дуже добре, оскільки надмірне зволікання може спричинити проблеми з довшими рядками. Я б скоріше написав нерегексивний метод для вирішення проблеми.
Wiktor Stribiżew

2
@ WiktorStribiżew Якщо я копіюю СравніваемаяСтроку в regexstorm кілька разів, що робить його величезну рядок, я отримую різницю в продуктивності , наприклад , геометричні шаблони 750ms, (.)(?<=(?:.*\1){3})25 мс, (.)(?<=(?:\1.*?){2}\1)3 мс. Можна просто випробувати себе. Ваші, здається, найменш ефективні, і це найважче для читання.
пузир бульбашки

Відповіді:


8

Нерегекс-R-розчин. Розділений рядок. Замініть елементи цього вектора, що мають рядок> = 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.


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:]- об'єднати підрядку до індексу з, -а потім підрядку після індексу, фактично пропустивши початковий символ.

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

Немає одношарового регекс-пітона:

s = "111aabbccxccybbzaa1"

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

Це перераховується через рядок, підраховуючи події поточного символу за ним і лише ставлячи символ, якщо він є одним із перших 2, інакше тире.


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

Завдяки Wiktor Stribiżew , Стефан Pochmann і пензлика міхур . Для завершення я розміщую можливі regexрішення, обговорені в коментарях;

Це можливо лише за допомогою регулярного виразка, який підтримує нескінченну ширину позаду. За допомогою модуля 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---

Уривок .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.