2012-07-11 10 views
7

正規表現を使ってテキストブロックからいくつかのものを削除しようとしています。私はすべてのパターンを準備していますが、重複する2つ(またはそれ以上)を取り除くことができないようです。例えば複数の正規表現の置換を組み合わせる

import re 

r1 = r'I am' 
r2 = r'am foo' 

text = 'I am foo' 

re.sub(r1, '', text) # Returns ' foo' 
re.sub(r2, '', text) # Returns 'I ' 

どのように私は同時に出現の両方を交換し、空の文字列で終わるのですか?


は私が Ned Batchelder's answerを少し変更したバージョンを使用して終了:

def clean(self, text): 
    mask = bytearray(len(text)) 

    for pattern in patterns: 
    for match in re.finditer(pattern, text): 
     r = range(match.start(), match.end()) 

     mask[r] = 'x' * len(r) 

    return ''.join(character for character, bit in zip(text, mask) if not bit) 

答えて

12

あなたが示したように、連続したre.subコールでは実行できません。あなたはre.finditerを使ってすべてを見つけることができます。それぞれの試合は一致オブジェクトを提供します。一致オブジェクトは、位置を示す.start.endの属性を持ちます。それらをまとめて収集し、最後に文字を削除することができます。

ここでは、可変文字列としてbytearrayをマスクとして使用しています。ゼロバイトに初期化され、正規表現にマッチするすべてのバイトを 'x'でマークします。それから私は、元の文字列に保つために文字を選択するために、ビットマスクを使用して、唯一無比の文字で新しい文字列を構築する:

bits = bytearray(len(text)) 
for pat in patterns: 
    for m in re.finditer(pat, text): 
     bits[m.start():m.end()] = 'x' * (m.end()-m.start()) 
new_string = ''.join(c for c,bit in zip(text, bits) if not bit) 
+0

私は、マッチオブジェクトの 'start'と' end'属性について考えたことはありませんでした。私はこれがうまくいくと確信していますので、ありがとう! – Blender

+1

すばらしい答え!これらはメソッドであり属性ではないので、 'start'と' end'に '()'を追加しました。 – georg

+0

@ thg435:ありがとう、私はそれをテストしたはずです! :) –

2

はダウナーをしてはならないが、短い答えは、私はあなたができないかなり確信しているということです。正規表現が重複しないように変更できますか?

これをやりたければ、元の文字列に一致するそれぞれの開始と終了のインデックスを追跡してみます。その後、文字列を通過し、削除範囲にない文字だけを保持しますか?

1

かなり効率的過ぎるから来るソリューションです... Perlの正規表現を組み合わせます1で:

# aptitude install regexp-assemble 
$ regexp-assemble 
I am 
I am foo 
Ctrl + D 
I am(?: foo)? 

正規表現は、組み立てが照合する正規表現または文字列のすべての亜種を取り、その後、 は1でそれらを組み合わせます。そして、それはもう、重複正規表現にマッチに関するものではありませんので、はい、それは別のものへの最初の問題を変更しますが、一致する正規表現を組み合わせること

、その後、あなたのコードでそれを使用することができます。

$ python 
Python 2.7.3 (default, Aug 1 2012, 05:14:39) 
[GCC 4.6.3] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import re 
>>> re.sub("I am foo","I am(?: foo)?","") 
'' 

のポートRegexp :: Assemble in python nice:

+0

コマンドは 'aptitude install libregexp-assemble-perl'です。あなたが指定した名前で以前のパッケージの痕跡をすばやく見つけることができませんでしたが、おそらくあなたは別のディストリビューションにいます。これはDebian安定版です。 – tripleee

+0

また、古いバージョンのパッケージでは、デモは '/ usr/share/doc/libregexp-assemble-perl/examples/assemble.gz'にしかありませんでした。これを' squeeze'ボックスに入れたかったので、あなたが指定した名前でインストールされていません。 – tripleee

1

セレクタイテレータを使ってテキスト上でitertools.compressを使用してオンザフライで文字列をフィルタリングする代替方法があります。文字を保持する必要がある場合、セレクタはTrueを返します。 selector_for_patternsは、すべてのパターンに対して1つのセレクタを作成します。セレクタはall関数と組み合わされます(すべてのパターンが結果の文字列内にある文字を保持したい場合のみ)。

import itertools 
import re 

def selector_for_pattern(text, pattern): 
    i = 0 
    for m in re.finditer(pattern, text): 
     for _ in xrange(i, m.start()): 
      yield True 
     for _ in xrange(m.start(), m.end()): 
      yield False 
     i = m.end() 
    for _ in xrange(i, len(text)): 
     yield True 

def clean(text, patterns): 
    gen = [selector_for_pattern(text, pattern) for pattern in patterns] 
    selector = itertools.imap(all, itertools.izip(* gen)) 
    return "".join(itertools.compress(text, selector)) 
関連する問題