Cocokkan dan hapus karakter yang digandakan: Ganti beberapa (3+) kejadian tidak berurutan

9

Saya mencari regexpola yang akan cocok dengan ketiga, keempat, ... kemunculan setiap karakter. Lihat di bawah untuk klarifikasi:

Misalnya saya punya string berikut:

111aabbccxccybbzaa1

Saya ingin mengganti semua karakter yang digandakan setelah kejadian kedua. Outputnya adalah:

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

Beberapa pola regex yang saya coba sejauh ini:

Dengan menggunakan regex berikut ini, saya dapat menemukan kemunculan terakhir dari setiap karakter: (.)(?=.*\1)

Atau menggunakan ini saya bisa melakukannya untuk duplikat berturut-turut tetapi tidak untuk duplikat: ([a-zA-Z1-9])\1{2,}

M--
sumber
1
Mesin regex apa yang Anda rencanakan untuk digunakan dengan regex?
Wiktor Stribiżew
1
Anda hanya dapat melakukannya dengan regex yang mendukung tampilan lebar tak terbatas di belakang, jadi satu-satunya pilihan Anda adalah modul regex Python PyPi. Gunakan dengan (.)(?<=^(?:(?:(?!\1).)*\1){2,}(?:(?!\1).)*\1)regex. Demo .
Wiktor Stribiżew
3
@ WiktorStribiżew Apakah lebih baik dari itu (.)(?<=(.*\1){3})?
Stefan Pochmann
2
@StefanPochmann Yah, (.)(?<=(?:.*\1){3})akan melakukan pekerjaan itu juga, tetapi semua ini tidak bagus karena pengulangan yang berlebihan dapat menyebabkan masalah dengan string yang lebih panjang. Saya lebih suka menulis metode non-regex untuk menyelesaikan masalah.
Wiktor Stribiżew
2
@ WiktorStribi Ifew Jika saya menyalin teststring ke regexstorm beberapa kali, menjadikannya string yang sangat besar, saya mendapatkan perbedaan kinerja misalnya pola Anda 750ms, (.)(?<=(?:.*\1){3})25ms, (.)(?<=(?:\1.*?){2}\1)3ms. Anda bisa saja menguji diri sendiri. Pola Anda tampaknya paling tidak efisien dan paling sulit dibaca.
gelembung bobble

Jawaban:

8

Solusi R non-regex. Split string. Ganti elemen dari vektor ini yang memiliki rowid> = 3 * with '-'. Rekatkan kembali.

x <- '111aabbccxccybbzaa1'

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

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

* rowid(x)adalah vektor integer dengan setiap elemen yang mewakili berapa kali nilai dari elemen terkait xtelah direalisasikan. Jadi jika elemen terakhir xadalah 1, dan itulah keempat kalinya 1terjadi x, elemen terakhir rowid(x)adalah 4.

IceCreamToucan
sumber
4

Anda dapat dengan mudah mencapai ini tanpa regex:

Lihat kode yang digunakan di sini

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)

Hasil:

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

Bagaimana ini bekerja:

  1. for u in set(s) mendapat daftar karakter unik dalam string: {'c','a','b','y','1','z','x'}
  2. for i in ... loop atas indeks yang kami kumpulkan 3.
  3. [i for i in range(len(s)) if s[i]==u][2:] lilitkan setiap karakter dalam string dan periksa apakah cocok u (dari langkah 1.), lalu iris array dari elemen ke-2 (menjatuhkan dua elemen pertama jika ada)
  4. Atur string untuk s[:i]+'-'+s[i+1:]- menggabungkan substring ke indeks dengan -dan kemudian substring setelah indeks, secara efektif menghilangkan karakter asli.
lihat roda
sumber
3

Opsi dengan 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---"

data

x <- '111aabbccxccybbzaa1'
akrun
sumber
2

Tidak ada regex python one-liner:

s = "111aabbccxccybbzaa1"

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

Ini menghitung melalui string, menghitung kemunculan karakter saat ini di belakangnya dan hanya menempatkan karakter jika itu adalah salah satu dari 2 yang pertama, jika tidak lari.

ParkerD
sumber
1

Cara lain untuk melakukannya 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())

Keluaran :

11-aabbccx--y--z---
CypherX
sumber
0

Terima kasih kepada Wiktor Stribiżew , Stefan Pochmann , dan gelembung berbandul . Demi penyelesaian, saya memposting regexsolusi yang mungkin dibahas dalam komentar;

Ini hanya dapat dilakukan dengan regex yang mendukung tampilan lebar tak terbatas di belakang. Menggunakan modul Python PyPi regex kita bisa melakukan hal berikut:

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

Cuplikan .

M--
sumber