JavaScriptの正則は他の言語とは異なるところがいくつかあります.

10914 ワード

多くの言語に接したことがあります.言語の正規表現が強いかどうか、また正則と文法の結合が緊密かどうかを重視しています.この点において、JavaScriptが作ったのは悪くないです.少なくとも正則字面の量があります.もちろん、最も強いのはPerlです.最近、JavaScriptの正則はあるところでの表現と他の言語またはツールの正則が少し違っていることが分かりました.他のタイプです.下の私が話したこれらの正則はほとんど書けないかもしれませんが、理解すればいいです.ここのコード例はES 5対応のJavaScript環境で実行されます.つまり、IE 9の前のバージョン、Fx 4ぐらいのバージョンなど、中の表現は私が話したのと違っている可能性があります.
1.空の文字クラス
他の言語では、このような書き方は不法です.すべての文書と教程は不法な文法を言わないと信じています.他の言語やツールはどうやってこの間違いを報告しますか?
$echo | grep '[]'
grep: Unmatched [ or [^

$echo | sed
'/[]/' sed:-e #14
$echo | awk
'/[]/' awk: cmd. line:1: /[]/ awk: cmd. line:1: ^ unterminated regexp awk: cmd. line:1: error: Unmatched [ or [^: /[]//
$echo | perl
-ne '/[]/' Unmatched [ in regex; marked by <-- HERE in m/[ <-- HERE ]/ at -e line 1.
$echo | ruby
-ne '/[]/' -e:1: empty char-class: /[]/
$python
-c 'import re;re.match("[]","")' Traceback (most recent call last): File "<string>", line 1, in <module> File "E:\Python\lib\re.py", line 137, in match return _compile(pattern, flags).match(string) File "E:\Python\lib\re.py", line 244, in _compile raise error, v # invalid expression sre_constants.error: unexpected end of regular expression
JavaScriptでは、空文字類は合法的な正則構成部分ですが、その効果は「いつまでもマッチしない」、つまり何にもマッチしないと失敗します.空の否定に相当します.正環視(empty negative lookate)(?!)の効果:
js> "whatever
".match(/[]/g)
// ,
null
js
> "whatever
".match(/(?!)/g)
// ,
null
明らかに、このようなものはJavaScriptの中ではあまり役に立ちません.
2.空の文字類を否定する
どの文字も含まれていない否定文字類(^^)は、空の文字類(negative empty char class)を否定したり、空の否定文字類(empy negative char class)を叫んだりしてもいいです.この名詞は私が「自作」したもので、上記の空の文字類と似ています.他の言語でも不法です.
$echo | grep '[^]'
grep: Unmatched [ or [^

$echo | sed
'/[^]/' sed:-e #15
$echo | awk
'/[^]/' awk: cmd. line:1: /[^]/ awk: cmd. line:1: ^ unterminated regexp awk: cmd. line:1: error: Unmatched [ or [^: /[^]//
$echo | perl
-ne '/[^]/' Unmatched [ in regex; marked by <-- HERE in m/[ <-- HERE ^]/ at -e line 1.
$echo | ruby
-ne '/[^]/' -e:1: empty char-class: /[^]/
$python
-c 'import re;re.match("[^]","")' Traceback (most recent call last): File "<string>", line 1, in <module> File "E:\Python\lib\re.py", line 137, in match return _compile(pattern, flags).match(string) File "E:\Python\lib\re.py", line 244, in _compile raise error, v # invalid expression sre_constants.error: unexpected end of regular expression $
JavaScriptでは、空の文字類を否定するのは合法的な正則の構成部分であり、その効果は空の文字類の効果とは正反対で、任意の文字にマッチすることができます.改行符「」を含みます.つまり、よく見られる「\s\S」と「w\W」と同じです.
js> "whatever
".match(/[^]/g) // ,
["w", "h", "a", "t", "e", "v", "e", "r", "
"]
js> "whatever
".match(/[\s\S]/g) // ,
["w", "h", "a", "t", "e", "v", "e", "r", "
"]
注意したいのは、これは「永整合正則」とは言えないです.文字類は必ず1つの文字があってこそマッチ可能です.ターゲット文字列が空の場合、または左の正則によって消耗された場合、マッチングは失敗します.
js> /abc[^]/.test("abc")     //c       ,    .
false
本当の「永整合正則」を知りたいです.前に翻訳した文章を見てもいいです.「空」正則
3.[]と[^]]
これは比較的簡単です.Perlと他のいくつかのlinuxコマンドの正規表現では、文字種類[]に左括弧に続く右側括弧[]が含まれていれば、この右側括弧は普通の文字として扱われます.即ち、「」のみにマッチします.JavaScriptでは、このような正則は空の文字の後の右側括弧として認識されます.空の文字類は何も一致しません.同様です.JavaScriptでは、任意の文字(空の文字類を否定する)の後に付いている右の中かっこです.たとえば、「a」、「b」などです.他の言語では非の文字にマッチします.
$perl -e 'print "]" =~ /[]]/'
1

$js -e 'print(/[]]/.test("]"))'
false

$perl -e 'print "x" =~ /[^]]/'
1

$js -e 'print(/[^]]/.test("x"))'
false 
4.$アンカーポイント
一部の初心者は$マッチングは改行符であると考えています.これは大間違いです.$はゼロ幅です.  aseration)は、本物の文字にマッチすることができないので、一つの位置にしかマッチできません.必要な違いは複数行モードで発生します.複数行以外のモードでは、$マッチングは最後の文字の後ろの位置ではないと思うかもしれません.実際にはそんなに簡単ではありません.他の大部分の言語では、ターゲット文字列の最後の文字が改行符の「」であれば、その改行符の前の位置、つまり末尾の改行符の左右の2つの位置にもマッチします.多くの言語では、「Z」と「z」の2つの表現があります.それらの違いを知っていれば、分かります.他の言語では$は\Zに相当しますが、Java Scriptではマルチラインモードではなく、\zに相当します.Rubyは、デフォルトのマルチラインモードでは$zに相当します.もちろんエンディングに出てくるかもしれない改行符も含まれています.
$perl -e 'print "whatever
" =~ s/$/ /rg
' //
whatever
// // $js -e 'print("whatever
".replace(/$/g," "))
' //
whatever
//
5.ドット記号「.」
JavaScriptの正規表現では、点記号の元文字「.」は4行の終端符(\r-エコー、-改行符、\u 2028-行の区切り記号、\u 2029-段落の区切り記号)以外のすべての文字にマッチしますが、他の常用語では改行記号は除外されます.
6.前へ引用する
正則には逆参照(back reference)があることを知っています.つまり、前のある捕獲パケットにマッチした文字列を逆斜め棒+数字で引用しています.再マッチングまたは代替結果として使用することが目的です.ただし、引用されたキャプチャパケットがまだ開始されていない場合は、逆参照を使用してください.どのようになりますか?例えば、正則/(\2){2}/、(a)は第二の捕獲グループですが、その左側にはそのマッチング結果を引用する\2を使用しています.正則は左から右にマッチングすると知っています.これは本節のタイトルを前に引用するカレンダーです.厳密な概念ではありません.では、次のコードはJavariptに戻りますか?
js> /(\2(a)){2}/.exec("aaa")
???
この質問に答える前に、他の言語の表現を見てみます.同じように、他の言語では、このように書いても基本的に無効です.
$echo aaa | grep '(\2(a)){2}'
grep: Invalid back reference

$echo aaa | sed
-r '/(\2(a)){2}/' sed:-e #112
$echo aaa | awk
'/(\2(a)){2}/'
$echo aaa | perl
-ne '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ではエラーが発生していません.なぜこのように設計されているのか分かりません.Perlを学んでいるはずですが、効果は同じです.このような状況ではマッチングできないです.
JavaScriptでは、エラーを報告しないだけでなく、成功にもマッチします.先ほどあなたが考えていた答えと同じではないです.
js> /(\2(a)){2}/.exec("aaa")
["aa", "a", "a"]
execメソッドの戻り値を忘れないように注意してください.最初の要素は完全にマッチした文字列で、つまりRegExp'''です.後は各キャプチャグループがマッチングした内容です.つまりRegExp.$1とRegExp.$2.なぜマッチングが成功したのですか?マッチングプロセスはどうなりますか?私の理解は:
最初のキャプチャパケット(一番左の括弧)に入りました.最初の有効一致項目は\2ですが、第二のキャプチャパケット(a)がまだラウンドされていないので、RegExp.$2の値はundefinedです.したがって、\2はターゲット文字列の最初のaの左側の空の文字と一致します.あるいは「位置」と言います.他のゼロ幅と断言するように.ポイントはマッチします.続けて歩きます.このとき、2番目のキャプチャパケット(a)は、ターゲット文字列の最初のaにマッチし、RegExp.$2の値も「a」に割り当てられ、そして最初のキャプチャパケットが終了し(一番右の括弧)、RegExp.$1の値も「a」である.そして、助数詞{2}である.つまり、ターゲット文字列の最初のaから正則(2)のラウンドのマッチングが開始される.重要な点はここです.RegExp.$2の値です.つまり\2の値は第1ラウンドのマッチングが終わった時の値ではないですか?ターゲット文字列の2番目のaとのマッチングに成功した場合、RegExp.$1とRegExp.$2の値が再び「a」になり、RegExp[$&]の値が完全なマッチング文字列となり、前の2つのa:「aa」になります.
Firefoxの初期バージョン(3.6)では、助数詞の再マッチングは、既存の獲得パケットの値をクリアしない.すなわち、第2ラウンドのマッチング時には、\2は第2のaと一致する.
js> /(\2(a)){2}/.exec("aaa")
["aaa", "aa", "a"]
また、1つのキャプチャパケットの終了は右括弧が閉じているかどうかを見ます.例えば、//(a\1){3}/は、\1を使用している間に、最初のキャプチャパケットがマッチングし始めましたが、まだ終わっていません.
js> /(a\1){3}/.exec("aaa")
["aaa", "a"]
もう一つの例を説明します.
js> /(?:(f)(o)(o)|(b)(a)(r))*/.exec("foobar")
["foobar", undefined, undefined, undefined, "b", "a", "r"]
*号は助数詞で、第1ラウンドのマッチング後は$1は"f"で、$2は"o"で、3は"o"で、4はundefinedで、5はundefinedで、6はundefinedです.
第2ラウンドマッチ開始時:キャプチャされた値をすべてundefinedにリセットします.
第2ラウンドマッチング後:$1はundefinedで、$2はundefinedで、3はundefinedで、$4は"b"で、5は"a"で、6は"r"です.
ドル&値が付けられました.
最後の思考問題:
js> /(?:^(a)|\1(a)|(ab)){2}/.exec("aab")
????