重複する文字を一致させて削除する:複数(3+)の連続しない出現箇所を置き換えます


9

regex各文字の3番目、4番目、...の出現に一致するパターンを探しています。明確にするために以下を見てください:

たとえば、次の文字列があります。

111aabbccxccybbzaa1

2回目以降の重複文字をすべて置き換えたい。出力は次のようになります。

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

これまでに試したいくつかの正規表現パターン:

次の正規表現を使用して、各文字の最後の出現を見つけることができます。 (.)(?=.*\1)

または、これを使用して、連続する重複に対してそれを行うことができますが、重複に対してはできません: ([a-zA-Z1-9])\1{2,}


1
どの正規表現エンジンを正規表現で使用する予定ですか?
WiktorStribiżew19年

1
無限幅の後読みをサポートする正規表現でのみそれを行うことができるため、その場合の唯一のオプションはPython PyPi正規表現モジュールです。(.)(?<=^(?:(?:(?!\1).)*\1){2,}(?:(?!\1).)*\1)正規表現で使用します。デモ
WiktorStribiżew19年

3
@WiktorStribiżewそれ以上(.)(?<=(.*\1){3})ですか?
Stefan Pochmann、

2
@StefanPochmannまあ、それでもうまく(.)(?<=(?:.*\1){3})いきますが、過度のバックトラックが長い文字列で問題を引き起こす可能性があるため、これらすべては適切ではありません。問題を解決するために、正規表現ではないメソッドを作成したいと思います。
WiktorStribiżew19年

2
@WiktorStribiżewテスト文字列をregexstormに数回コピーして巨大な文字列にすると、パターンに750ミリ秒、(.)(?<=(?:.*\1){3})25ミリ秒、3 (.)(?<=(?:\1.*?){2}\1)ミリ秒などのパフォーマンスの違いが生じます。自分でテストできます。あなたのパターンは最も効率の悪いパターンのようで、読むのが最も難しいです。
ボブルバブル

回答:


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、それがで4回目の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番目の要素から最後までスライスします(最初の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

正規表現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文字の1つである場合にのみ文字を置きます。それ以外の場合はダッシュです。


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に、コメントで議論された可能な解決策を掲載しています。

これは、無限幅の後読みをサポートする正規表現でのみ実行できます。Python PyPi regexモジュールを使用すると、次のことができます。

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