จับคู่และลบอักขระที่ซ้ำซ้อน: แทนที่เหตุการณ์ที่ไม่ต่อเนื่องหลายรายการ (3+) ติดต่อกัน


9

ฉันกำลังมองหาregexลวดลายที่จะเข้าคู่กับตัวละครตัวที่สามสี่ ... ตัวละครแต่ละตัว ดูคำอธิบายด้านล่าง:

ตัวอย่างเช่นฉันมีสตริงต่อไปนี้:

111aabbccxccybbzaa1

ฉันต้องการแทนที่อักขระที่ซ้ำกันทั้งหมดหลังจากที่เกิดขึ้นครั้งที่สอง ผลลัพธ์จะเป็น:

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

บางรูปแบบของ regex ที่ฉันลองมา:

ใช้ regex ต่อไปนี้ฉันสามารถค้นหาการเกิดขึ้นครั้งสุดท้ายของตัวละครแต่ละตัว: (.)(?=.*\1)

หรือใช้อันนี้ฉันสามารถทำได้เพื่อทำซ้ำติดต่อกัน แต่ไม่ใช่สำหรับการทำซ้ำ: ([a-zA-Z1-9])\1{2,}


1
เอนจิ้น regex ใดที่คุณวางแผนที่จะใช้กับ regex
Wiktor Stribiżew

1
คุณสามารถทำได้ด้วย regex ที่รองรับความกว้างที่ไม่มีที่สิ้นสุดดังนั้นตัวเลือกเดียวของคุณคือโมดูล Python PyPi regex ใช้กับ(.)(?<=^(?:(?:(?!\1).)*\1){2,}(?:(?!\1).)*\1)regex การสาธิต
Wiktor Stribiżew

3
@ WiktorStribiżewมันดีกว่า(.)(?<=(.*\1){3})เหรอ?
Stefan Pochmann

2
@StefanPochmann เอาล่ะ(.)(?<=(?:.*\1){3})ก็จะทำงานเช่นกัน แต่สิ่งเหล่านี้ไม่ดีเนื่องจากการย้อนรอยมากเกินไปอาจทำให้เกิดปัญหากับสายอักขระที่ยาวกว่า ฉันอยากจะเขียนวิธีที่ไม่ใช่ regex เพื่อแก้ปัญหา
Wiktor Stribiżew

2
@ WiktorStribiżewถ้าฉันคัดลอก teststring ลงใน regexstorm หลายครั้งทำให้เป็นสตริงขนาดใหญ่ฉันได้รับประสิทธิภาพที่แตกต่างเช่นรูปแบบของคุณ 750ms, (.)(?<=(?:.*\1){3})25ms, (.)(?<=(?:\1.*?){2}\1)3ms คุณสามารถทดสอบด้วยตัวเอง คุณดูเหมือนจะเป็นรูปแบบที่มีประสิทธิภาพน้อยที่สุดและอ่านยากที่สุด
ฟองสบู่กลมเล็ก

คำตอบ:


8

วิธีแก้ปัญหา Non-regex แยกสตริง เปลี่ยนองค์ประกอบของเวกเตอร์นี้มี 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


4

คุณสามารถทำได้โดยไม่ต้อง regex:

ดูรหัสการใช้งานที่นี่

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

ไม่มีงูเหลือม regex หนึ่งซับ:

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และBubble Bobble เพื่อความสมบูรณ์ฉันได้โพสต์regexวิธีแก้ไขที่เป็นไปได้ที่กล่าวถึงในความคิดเห็น

สิ่งนี้สามารถทำได้กับ regex ที่รองรับความกว้างที่ไม่มีที่สิ้นสุด การใช้ Python PyPi regex module เราสามารถทำสิ่งต่อไปนี้:

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

เศษเล็กเศษน้อย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.