8

正規表現を使用して、テキストのブロックからいくつかのものを削除しようとしています。すべてのパターンの準備ができていますが、重なっている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 '

両方のオカレンスを同時に置き換えて、空の文字列になってしまうにはどうすればよいですか?


私はNedBatchelderの答えのわずかに修正されたバージョンを使用することになりました:

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)
4

4 に答える 4

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)
于 2012-07-11T22:39:55.417 に答える
2

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

それでもこれを実行したい場合は、元の文字列で行われた各一致の開始インデックスと終了インデックスを追跡してみます。次に、文字列を調べて、削除範囲内にない文字のみを保持しますか?

于 2012-07-11T22:39:13.397 に答える
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))
于 2012-09-27T13:58:06.327 に答える
1

非常に効率的なのは、...Perlが正規表現を1つに組み合わせたソリューションです。

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

regexp-assembleは、一致させたい正規表現または文字列のすべてのバリアントを取得し、それらを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)?","")
''

pythonでのRegexp::Assembleの移植は素晴らしいでしょう:)

于 2012-09-09T09:54:42.813 に答える