JavaScriptの正則と他の言語の違いをまとめます.

7918 ワード

前言
最近JavaScriptの中の正則は他の言語やツールの中の正則とは少し違っています.あなたがほとんど書くことができなくても、以下のような正しい規則はほとんど使えません.でも、知っておくといいです.
本論文のコード例は、ES 5対応のJavaScript環境で実行されるものであり、つまり、IE 9の前のバージョン、Fx 4のバージョンなど、中の表現は以下の説明とは異なる可能性が高い.
1.空の文字クラス
他の言語では、このような書き方は不法です.すべての文書と教程は不法な文法を話しません.他の言語やツールはどのようにこの間違いを報告するかを示します.

$echo | grep '[]'
grep: Unmatched [ or [^

$echo | sed '/[]/'
sed:-e     #1,   4:           

$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 ", line 1, in 
 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では、空文字類は合法的な正則構成部分であるが、その効果は「永遠にマッチしない」、つまり何にもマッチしないと失敗します.空の否定に相当する正のループ[]の効果:

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     #1,   5:           

$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 ", line 1, in 
 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 char classを含む任意の文字にマッチすることができます.すなわち、一般的な(empty negative lookahead)(?!)"
"
と同じです.

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コマンドの正規表現では、文字クラス[\s\S]に左括弧に続く右側括弧[\w\W]が含まれていると、この右側括弧は普通の文字、すなわち「」のみとして扱われ、JavaScriptでは、このような正則が空の文字の後側の括弧として認識されます.空の文字類は何も一致しません.[]も同様です.JavaScriptでは、任意の文字(空の文字を否定するクラス)の後に付いている右の中の括弧です.

$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)があることを知っています.つまり、前のある捕獲パケットにマッチした文字列を逆斜め棒+数字で引用しています.再マッチングまたは代替結果として使用することが目的です.ただし、引用されたキャプチャパケットがまだ開始されていない場合は、逆参照を使用してください.どのようになりますか?例えば、正規"a]","b]"、(a)は第二の捕獲パケットですが、その左側にはそのマッチング結果を引用する「2」が使用されています.正は左から右にマッチングすると知っています.これは本節のタイトルが前に引用された由来です.厳密な概念ではありません.では、次のJavaScriptコードは何に戻りますか?

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     #1,   12:     

$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"]
"
"
方法で戻ってきた結果が何だったかを忘れないようにします.一番目の要素は完全なマッチング文字列、つまり"
"
です.後は各キャプチャパケットがマッチングした内容です.つまり/(\2(a)){2}/ execはなぜマッチングできたのですか?マッチングプロセスはどうなりますか?私の理解は:
最初のキャプチャパケット(一番左の括弧)に入りました.最初の有効一致項目は\2です.しかし、第二のキャプチャパケット(a)はまだラウンドされていないので、RegExp["$&"] の値はRegExp.$1です.したがって、\2はターゲット文字列の最初のaの左側の空の文字と一致しました.あるいは、「位置」と言います.他のゼロ幅と断言するように.ポイントはマッチしました.引き続き歩きます.このとき、第2の獲得パケット(a)は、ターゲット文字列の第1のaにマッチし、RegExp.$2.の値も「a」に割り当てられ、次いで第1の獲得パケットが終了し(一番右の右括弧)、RegExp.$2の値も「a」である.そして、助数詞{2}である.つまり、対象文字列の第1のaの後から、正則undefinedの新しいラウンドマッチングが開始される.重要な点はここです.RegExp.$2の値です.つまり、\2マッチの値ですか?それとも第1ラウンドマッチが終わった時の割り当てられた値「a」ではありませんか?答えは「いいえ」です.RegExp.$1(\2(a))の値は全部空にされます.ターゲット文字列の2番目のaとのマッチングに成功した場合、RegExp.$2RegExp.$1の値は再び「a」となり、RegExp.$2の値は完全なマッチング文字列となり、前の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はRegExp.$1です.
第2ラウンドマッチ開始時:取り込まれた値は全部RegExp.$2にリセットされます.
第2ラウンドのマッチング後、$1はRegExp["$&"]で、$2はundefinedで、$3はundefinedで、$4は"b"で、$5は"a"で、$6は"r"です.
&値を付けられてフォロワーとなり、マッチングは終了します.
締め括りをつける
以上はJavaScriptの正則と他の言語との違いをまとめた内容です.本文の内容が皆さんの学習と仕事に役立つことを期待しています.