私は多くの言語に触れてきましたが、その言語の正規表現が強力であるかどうか、および正規表現と文法の組み合わせが近いかどうかを非常に重視しています。現時点では、JavaScript は、少なくとも通常のリテラルではかなりうまく機能しています。もちろん、最も強力なのは Perl です。最近、JavaScript の正規表現の動作が他の言語やツールの正規表現とは若干異なる点があり、まったく異なることに気づきました。以下で説明する通常のルールを作成することはほとんど不可能であり、使用することもほとんどありませんが、結局のところ、それらを理解するのは良いことです。この記事のコード例はすべて ES5 と互換性のある JavaScript 環境で実行されており、つまり IE9 以前のバージョンや Fx4 あたりのバージョンでは、以下で説明するパフォーマンスとは異なる可能性があります。
1. 空の文字クラス
文字 [] を含まない文字クラスは、他の言語ではこれを空の文字クラス (空の文字クラス) と呼ぶのを聞いたことがないでしょう。この種の記述方法は不正であり、他の言語やツールがこのエラーをどのように報告するかを示してみましょう。
grep: 一致しません [ または [^
$echo | sed '/[]/'
sed: -e 式 #1、文字 4: 未終了のアドレス正規表現
$echo | []/'
awk: cmd. line:1: /[]/
awk: cmd. line:1: ^ 終了していない正規表現
awk: cmd. line:1: エラー: 一致しません [ または [^ : / []//
$echo | perl -ne '/[]/'
-e 行 1 で <-- HERE でマークされた一致しない [ .
$echo | Ruby -ne '/[]/'
-e:1: 空の文字クラス: /[]/
$python -c 'import re;re.match ("[]","")'
トレースバック (最新の呼び出しは最後):
ファイル "
ファイル "E:Pythonlibre.py"、行137、match
return _compile(pattern, flags).match(string)
ファイル "E:Pythonlibre.py"、244 行目、_compile
でエラーが発生、v # 無効な式
sre_constants.error: Expected正規表現の終わり
JavaScript では、空の文字クラスは正当な正規コンポーネントですが、その効果は「決して一致しない」、つまり、空の否定前方参照と同等です。 (?!) 効果:
js> "whatevern".match(/[]/g) //空の文字クラス、一致しません
null
js> "whatevern".match(/ (?!) /g) //Null は前方参照を否定し、一致しません
null
明らかに、この種のことは JavaScript では役に立ちません。
2. 空の文字クラスを否定します
には文字が含まれません。否定文字クラス [^ ] は、negative empty char クラスまたは empty negative char クラスと呼ぶことができます。どちらにしても、この用語は私の「独自の作成」であり、上で述べたことと同じであるためです。空文字クラスも同様であり、この記述方法は次のとおりです。他の言語でも不正です:
$echo | grep '[^]'
grep: Unmatched [ or [^
$echo | sed '/[^ ]/'
sed: -e 式#1、文字 5: 終端されていないアドレス正規表現
$echo | awk '/[^]/'
awk: cmd. line:1: / [^]/
awk: cmd. ^ 終了していない正規表現
awk: cmd. line:1: エラー: 一致しません [または [^: /[^]//
$echo | perl - ne '/[^]/'一致しません [ ; <-- HERE in m/[ <-- HERE ^]/ at -e 1. $echo -ne '/[^]/'-e: 1: 空の文字クラス: /[^]/ $python -c 'import re;re.match("[^]","")' トレースバック (最新の呼び出し最後):ファイル「文字クラスには一致する 1 文字が必要であるため、「常に一致する正規表現」とは言えないことに注意してください。ターゲット文字列が空であるか、左側の正規表現によって消費されている場合、一致は失敗します。例:
js> /abc[^]/.test("abc") // c の後に文字がないため、一致は失敗します
false
本当の " を知りたい場合。常に一致する正規表現"。以前に翻訳した記事をご覧ください。「空の」正規表現
3.[]] と [^]]
これは比較的簡単に言うことができます。つまり、正規表現でPerl およびその他の Linux コマンドでは、文字クラス [] に右角括弧 []] とそれに続く左角括弧が含まれている場合、右角括弧は通常の文字として扱われます。つまり、「] のみに一致します」 " であり、JavaScript では、この正規表現は、右角かっこが後に続く空の文字クラスとして認識されます。空の文字クラスは何も一致しません。[^]] も同様です。JavaScript では、任意の文字 (否定) と一致します。空の文字クラス) の後に「a]」、「b]」などの右括弧が続き、他の言語では、] 以外の文字が一致します。
$perl -e 'print "]" = ~ /[]]/ '
$js -e 'print(/[]]/.test("]"))'
false
$perl -e 'print "x" =~ /[^] ]/'
$js -e 'print(/[^]]/.test("x"))'
false
4.$ アンカー
一部の初心者$ は改行文字 "n" と一致すると考えますが、これは完全に間違っています。$ はゼロ幅アサーションです。実際の文字と一致するのは 1 つの位置だけです。非複数行モードでは、$ は最後の文字の後の位置と一致しないのではないかと思われるかもしれません。実際、他のほとんどの言語では、これはそれほど単純ではありません。ターゲット文字列の最後の文字が改行文字「n」の場合、$ はその改行文字の前の位置とも一致します。つまり、最後の改行文字 Z と z の左側と右側の 2 つの位置と一致します。これら 2 つの表記法の違いがわかれば、他の言語 (Perl、Python、php、Java、c#...) では、非複数行モードの $ が同等であることが理解できるはずです。から Z、JavaScript では、非複数行モードの $ は z と同等です (最後の文字が改行文字であるかどうかに関係なく、最後の位置にのみ一致します)。Ruby はデフォルトで複数行モードに設定されているため、特殊なケースです。複数行モード 次の $ は、各改行文字の前の位置に一致します。もちろん、最後に表示される改行文字も含まれます。これらの点は、Yu Sheng 氏の書籍「レギュラーガイド」にも記載されています。
$perl - e 'print "whatevern" =~ s/$/置換文字/rg' //グローバル置換任意の置換文字 //改行文字の前の位置置換文字-S $ js -e 'proprint ("Whatvern" .replace (/$/g, "replace Character")' // グローバル置換 whatever 置換文字 // 5. 前方参照後方参照があることは誰もが知っています。正規表現では、以前のキャプチャ グループによって一致した文字列を参照するためにバックスラッシュ + 数値を使用します。目的は、再度一致するか、置換結果として使用されます ($ になります)。 、参照されたキャプチャ グループがまだ開始されておらず (左括弧が囲まれている)、後方参照が使用された場合はどうなりますか。たとえば、通常の /(2(a)){2}/、(a) は 2 番目のキャプチャ グループです。ですが、その左側では、通常の一致が左から右に一致することを参照するために使用されます。これは、このセクションのタイトルである前方参照の由来です。厳密な概念です。それでは、次の JavaScript コードが何を返すかを考えてください: js> /(2( a)){2}/.exec("aaa")??? これに答える前に、質問として、まず他の言語でのパフォーマンスを見てください。同様に、他の言語でも、この方法で記述すると、基本的に無効な grep '(2(a)){2}' }/'sed: -e 式 #1、文字 12 になります。 : 不正な後方参照 $echo aaa | awk '/(2(a)){2}/' $echo aaa | 'print /(2(a)){2}/' $echo aaa |ruby -ne 'print $_ = ~/(2(a)){2}/' $python -c 'import re;print re.match("( 2(a)){2}","aaa")'None awk は逆参照をサポートしていないため、awk ではエラーは報告されません。2 は ASCII コード 2 の文字として解釈されます。Perl、Ruby ではPython ではエラーは報告されません。なぜこのように設計されているのかはわかりませんが、この場合、結果は同じです。JavaScript では、エラーが報告されないだけでなく、答えが今考えたものと同じかどうかを確認できます。
js> /(2(a)){2 }/.exec("aaa" )
["aa", "a", "a"]
exec メソッドによって返される結果が何であるかを忘れた場合のために説明しておきます。最初の要素は完全な一致文字列 (RegExp[" $&"] に続いて、各キャプチャ グループの一致するコンテンツ、つまり RegExp.$1 と RegExp.$2) が一致するのはなぜですか?は:
最初のキャプチャ グループ (一番左の左括弧) に入力され、最初の有効な一致は 2 です。ただし、2 番目のキャプチャ グループ (a) はこの時点ではまだ切り上げられていないため、値はRegExp.$2 はまだ定義されていないため、2 が一致します。 ^ やその他のゼロ幅アサーションと同様に、ターゲット文字列の最初の a の左側にある null 文字、または「位置」が一致します。続行すると、2 番目のキャプチャ グループ (a) が一致します。ターゲット文字列の最初の a で、RegExp.$2 の値も「a」に割り当てられ、次に最初のキャプチャ グループ (右端) の最後に割り当てられます。右括弧)、RegExp.$1 の値も "a" です。次に、量指定子 {2} があります。これは、ターゲット文字列の最初の a の後で、新しいラウンドの正規一致 (2(a)) が開始されることを意味します。重要な点はここです: RegExp の値は 2 の値と一致しますか、それとも最初の一致の最後に割り当てられた値「a」ですか? 答えは「いいえ」です。 .$1 と RegExp.$2 は未定義にクリアされ、1 と 2 は最初と同じになり、null 文字との一致に成功します (効果なしと同等、書き込みの有無は同じです)。ターゲット文字列、次に RegExp.$1 と RegExp.$2 の値は再び "a" になり、RegExp["$&"] の値は完全に一致する文字列になります (最初の 2 つは "aa")。 Firefox の初期バージョン (3.6)、数量子 新しいマッチング ラウンドでは、既存のキャプチャ グループの値はクリアされません。つまり、2 回目のマッチングでは、2 が 2 番目の a と一致します。つまり、
です。 js> /(2(a )){2}/.exec("aaa")
["aaa", "aa", "a"]
さらに、キャプチャ グループの終わりは、 /(a1){ 3}/ のように、右括弧は閉じています。ただし、1 を使用すると、最初のキャプチャ グループの一致が開始されていますが、まだ終了していません。これも前方参照であるため、1 の一致はそのままです。空:
js> /(a1 ){3}/.exec("aaa")
["aaa", "a"]
別の例を説明します:
js> /(?: (f)(o)(o)| (b)(a)(r))*/.exec("foobar")
["foobar"、未定義、未定義、未定義、"b"、"a"、 "r"]
* は最初のマッチング後の量指定子です: $1 は "f"、$2 は "o"、$3 は "o"、$4 は未定義、$5 は未定義、$6 は未定義です。 2 回目のマッチングの開始時: キャプチャされた値 すべてが未定義にリセットされます。
2 回目のマッチング後: $1 は未定義、$2 は未定義、$3 は未定義、$4 は "b"、$5 は "a "、$6 は "r" です。
$& が割り当てられます。 "foobar" の場合、マッチは終了します。
最後の質問:
js> /(?:^(a)|1(a) |(ab)){2}/.exec("aab ")
????