1

私は貪欲ではない否定的な一致をしようとしていますが、それもキャプチャする必要があります。これらのフラグを Python で使用しています。re.DOTALL | re.LOCALE | re.MULTILINE、各フィールドがバックスラッシュの新しい行で始まるいくつかのテキスト ファイル 'データベース' の複数行のクリーンアップを実行します。各レコードは \lx フィールドで始まります。

\lx foo
\ps n
\nt note 1
\ps v
\nt note
\ge happy
\nt note 2
\ge lonely
\nt note 3
\ge lonely
\dt 19/Dec/2011

\lx bar
...

各 \ge フィールドには、そのレコード内のどこかに \ps フィールドが 1 つずつあることを確認しようとしています。現在、1 つの \ps の後に複数の \ge が続くことが多いため、上記の 2 つの単独の \ge のようにコピーする必要があります。

必要なロジックのほとんどは次のとおりです。任意の \ps フィールドの後で、別の \ps または \lx に遭遇する前に、\ge を見つけ、次に別の \ge を見つけます。\ps フィールドを 2 番目の \ge の直前までコピーできるように、すべてをキャプチャします。

そして、これが私の非機能的な試みです。これを置き換えます:

^(\\ps\b.*?\n)((?!^\\(ps|lx)*?)^(\\ge.*?\n)((?!^\\ps)*?)^(\\ge.*?\n)

これとともに:

\1\2\3\4\1\5

小さなファイル (34 行) でもメモリ エラーが発生します。もちろん、これが機能したとしても、3 番目または 4 番目の \ge ではなく 2 番目の \ge を処理しようとしているだけなので、複数回実行する必要があります。ですから、その点でのアイデアは私にも興味があります。

更新:少し調整が必要な場合もありましたが、Alan Moore のソリューションはうまく機能しました。悲しいことに、DOTALL をオフにする必要がありました。そうしないと、最初の .* を後続の \ps フィールドを含めて防ぐことができなかったからです。形。しかし、正規表現のドット情報で、(?s) 修飾子について知ってうれしく思いました。これにより、一般的に DOTALL をオフにすることができましたが、それ不可欠な他の正規表現では引き続き使用できます。

これは、私が必要とする1行の形式に要約された、推奨される正規表現です:

^(?P<PS_BLOCK>(?P<PS_LINE>\\ps.*\n)(?:(?!\\(?:ps|lx|ge)).*\n)*\\ge.*\n)(?P<GE_BLOCK>(?:(?!\\(?:ps|lx|ge)).*\n)*\\ge.*\n)

それは機能しましたが、上記の例を変更すると、「注 2」の上に \ps が挿入されました。また、\lxs および \ge2 を \lx および \ge と同じように扱っていました (いくつかの \b が必要でした)。だから、私は少し微調整されたバージョンを使いました:

^(?P<PS_BLOCK>(?P<PS_LINE>\\ps\b.*\n)(?:(?!\\(?:ps|lx|ge)\b).*\n)*\\ge\b.*\n)(?P<AFTER_GE1>(?:(?!\\(?:ps|lx|ge)\b).*\n)*)(?P<GE2_LINE>\\ge\b.*\n)

およびこの置換文字列:

\g<PS_BLOCK>\g<AFTER_GE1>\g<PS_LINE>\g<GE2_LINE>

再度、感謝します!

4

2 に答える 2

1

regexenで問題が発生し、「複数回実行する必要がある」と自分に言い聞かせるときはいつでも、パーサーを作成する必要があることを明確に示しています:-)

言語はかなり規則的であるように思われるので、パーサーは、おそらく次のような簡単なものから始めて、簡単に記述できるはずです。

def parse_line(line):
    kind, value = line.split(' ', 1)  # split on the first space
    kind = kind[1:]                   # remove the \
    parsed_value = globals().get('parse_' + kind, lambda x:x)(value)
    return (kind, parsed_value)

def parse_dt(value):
    val = ... # create datetime.date() from "19/Dec/2011"
    return val

ステートマシンを作成するのに使用するには少し可愛すぎるかもしれませんglobals()が、ボイラープレートコードを大量に節約できます... :-)

入力をタプルのリストに変換します。

records = [parse_line(line) for line in open("myfile.dta")]

("ps", ..)タプルの前にタプルが常に存在するかどうかを判断するのは("ge", ..)非常に簡単です。たとえば、最初にすべてのlxタプルがどこにあるかを確認します...

于 2012-08-12T22:26:15.330 に答える
1

DOTALLフラグを使用したため、メモリエラーが発生しています。データが実際に表示されたとおりにフォーマットされている場合は、とにかくそのフラグは必要ありません。デフォルトの動作はまさにあなたが望むものです。貪欲でない修飾子 ( ) も必要ありません?

この正規表現を試してください:

prog = re.compile(r"""
    ^
    (?P<PS_BLOCK>
      (?P<PS_LINE>\\ps.*\n)
      (?:                   # Zero or more lines that
        (?!\\(?:ps|lx|ge))  # don't start with
        .*\n                # '\ps', '\lx', or '\ge'...
      )*
      \\ge.*\n              # ...followed by a '\ge' line.
    )
    (?P<GE_BLOCK>
      (?:                   # Again, zero or more lines
        (?!\\(?:ps|lx|ge))  # that don't start with
        .*\n                # '\ps', '\lx', or '\ge'...
      )*
      \\ge.*\n              # ...followed by a '\ge' line.
    )
    """, re.MULTILINE | re.VERBOSE)

置換文字列は次のようになります。

r'\g<PS_BLOCK>\g<PS_LINE>\g<GE_BLOCK>'

まだ複数のパスを実行する必要があります。Python がサポート\Gしている場合は必要ありませんが、戻り値を使用subnして確認できます。number_of_subs_made

于 2012-08-12T23:52:03.883 に答える