web-dev-qa-db-ja.com

正規表現で「後読みアサーションは固定長でなければならない」の技術的理由は何ですか?

たとえば、以下の正規表現は失敗の報告を引き起こしますアサーションが固定されていない後読み

#(?<!(?:(?:src)|(?:href))=["\']?)((?:https?|ftp)://[^\s\'"<>()]+)#S

このような制限はlookaheadには存在しません。

44
wamp

先読みと後読みは、それらの名前が示すほど類似していません。先読み式は、スタンドアロンの正規表現の場合とまったく同じように機能しますが、現在の一致位置に固定され、一致するものを消費しません。

後読みはまったく別の話です。現在の一致位置から始めて、一度に1文字ずつテキストを逆方向に移動し、各位置で式の一致を試みます。一致する可能性がない場合は、後読みは途中で終了する前に、テキストの先頭(一度に1文字、覚えておく)まで移動する必要があります。これを、1回だけ適用される先読み式と比較してください。

もちろん、これは非常に単純化しすぎて、すべてのフレーバーがそのように機能するわけではありませんが、あなたはアイデアを思いつきます。先読みが適用される方法は、先読みが適用される方法とは根本的に異なります(そしてmuchより効率的ではありません)。後読みがどこまでさかのぼる必要があるかを制限することは意味があります。

72
Alan Moore

まず、これはすべての正規表現ライブラリ(.NETなど)には当てはまりません。

PCREの場合、理由は次のようです。

後読みアサーションの実装では、代替案ごとに、現在の位置を一時的に固定幅だけ戻し、次に一致を試みます。

(少なくとも http://www.autoitscript.com/autoit3/pcrepattern.html によると)。

11
mbeckish

PCREは、パフォーマンスの大きな問題を引き起こす可能性があるため、フローティング後読みをサポートしていません。これは、右から左へのマッチング機能がないためです。PCREは固定左からのみ分岐を開始できますが、可変長後読みの左は固定できません。

一般に、可能であれば、後読み部分を固定長のパターンに分岐させます。たとえば、代わりに:

(?<=(src|href)=")etc.

(1) これを使って:

(?:(?<=src=")|(?<=href="))etc.

(2) または\K

(src|href)="\Ketc.

ご了承ください \Kは、常に前の一致の終わりから検索を開始するため、実際の後読みではありません(前の一致への潜在的なバックステップがないため)。

(3) 一部の複雑な後読みのみのケースでは、逆ストリングの「逆」先読み式で検索できます。あまりエレガントではありませんが、うまくいきます:

.cte(?="=(ferh|crs))
6
Dávid Horváth

同じ問題が発生し、_(?: subexpression)_を使用して修正しました

非キャプチャグループを定義します。例:Write(?:Line)? "Console.WriteLine()"の "WriteLine" "Console.Write(value)"の "Write"

_,_ orの前にキャッチすると想定されている以下の正規表現を変更する必要がありました後ろのアサーションは固定長ではありません

_(?<=,|^)
_

これとともに、

_(?:(?<=,)|^)
_
2
Mehrad
grep -P '(?<=((three)|(one)) )two' <<< "one two three three two one"
grep: lookbehind assertion is not fixed length

grep -P '((?<=(three) )|(?<=(one) ))two' <<< "one two three three two one"
one two three three two one

PCREは処理効率のために、右から左へのマッチングまたは再帰をサポートしていません。 PCREが後読みを行う場合、以前の一致する文字列の末尾を検索します。可変サイズの一致を実装するには、再帰が必要であり、効率が低下します。参照: Look Behind Assertions

0
user4829160