2009-10-05 5 views
187

グローバルフラグと大文字と小文字を区別しないフラグを使用すると、この正規表現の問題は何ですか?クエリーはユーザが生成した入力です。結果は[真、真]でなければなりません。グローバルフラグ付きRegExpで間違った結果が得られるのはなぜですか?

var query = 'Foo B'; 
var re = new RegExp(query, 'gi'); 
var result = []; 
result.push(re.test('Foo Bar')); 
result.push(re.test('Foo Bar')); 
// result will be [true, false] 

var reg = /^a$/g; 
 
for(i = 0; i++ < 10;) 
 
    console.log(reg.test("a"));

+36

JavaScriptのRegExpの多くのトラップの1つにようこそ。それは私が今までに会ったことのない正規表現の処理のための最悪のインターフェースの1つを持ち、奇妙な副作用とあいまいな警告がいっぱいです。正規表現を使用して一般的に行う一般的な作業のほとんどは、正しく綴りにくいです。 – bobince

+0

XRegExpは良い選択肢のようです。 http://xregexp.com/ – about

+0

こちらの回答もご覧ください:http://stackoverflow.com/questions/604860/interesting-test-of-javascript-regexp – Prestaul

答えて

245

ではなく、最後に使用されたインデックスから始まります、その後の試合のようRegExpオブジェクトは、マッチが発生したlastIndexを追跡します0のを見てみましょう:

var query = 'Foo B'; 
var re = new RegExp(query, 'gi'); 
var result = []; 
result.push(re.test('Foo Bar')); 

alert(re.lastIndex); 

result.push(re.test('Foo Bar')); 

手動ですべてのテストの後に0にlastIndexをリセットしたくない場合は、単に0を削除フラグ。

RegExp.prototype:

はここでスペックは(セクション15.10.6.2)を規定するアルゴリズムです。EXEC(文字列)

行う 正規表現に対して文字列 の正規表現一致と は 列(ストリング のToStringと一致しなかった場合、またはnull試合の 結果を含むArrayオブジェクトを返します次のように文字列)が正規表現 パターンの発生のために検索される:

  1. SはToStringメソッド(文字列)の値とします。
  2. 長さをSとする。
  3. lastIndexをlastIndexプロパティの値とする。
  4. iをToInteger(lastIndex)の値とします。
  5. グローバルプロパティがfalseの場合は、i = 0とします。
  6. 0またはI> lengthの場合、lastIndexを0に設定してnullを返します。
  7. [[Match]]を呼び出して、引数Sとiを与えます。 [[Match]] が失敗を返した場合は、手順8に進みます。 それ以外の場合は、rをその状態結果 とし、ステップ10に進みます。
  8. i = i + 1とします。
  9. 手順6に進みます。
  10. eをrのendIndex値とします。
  11. グローバルプロパティがtrueの場合は、lastIndexをeに設定します。
  12. nをrのキャプチャ配列の長さとします。 (これは、15.10.2.1の NCapturingParens同じ 値である。)
  13. 戻り、次の特性を有する新しい配列:
    • 性が内 マッチした部分文字列の位置に設定されているインデックス完全 ストリングS.
    • 入力プロパティが設定されている
    • Sにlengthプロパティを N + 1
    • 0プロペに設定されていますrtyは と一致する部分文字列(つまり、オフセットiとオフセット との間の部分とオフセットeとの間の の部分)。
    • i> 0かつI≦nの場合、 は、ToString(i)という名前のプロパティを のrのキャプチャ配列のi番目の要素に設定します。
+39

これは、Hitchhiker's Guide to Galaxy APIデザインのようなものです。 「あなたが落としたその落とし穴は、あなたがチェックするのを悩んでいただけで、数年にわたって仕様に完全に文書化されています。 – Retsam

+4

Firefoxのスティッキー・フラッグは、まったく何を意味するものでもありません。むしろ、正規表現の先頭に^があるかのように動作します。ただし、^が文字列の先頭ではなく現在の文字列の位置(lastIndex)と一致する場合を除いて動作します。正規表現が「lastIndexの後ろのどこでも」の代わりに「ここに」一致するかどうかを効果的にテストしています。あなたが提供したリンクを参照してください! – Doin

+0

この回答の開始文は正確ではありません。あなたは、何も言わない仕様のステップ3を強調表示しました。 'lastIndex'の実際の影響は、ステップ5,6,11にあります。あなたのopening文は、グローバルフラグが設定されている場合にのみ真です。 – Prestaul

59

あなたは、単一のRegExpオブジェクトを使用し、それを複数回実行しています。連続して実行するたびに、最後の一致インデックスから継続します。

あなたはそれぞれの実行前に、最初から開始するように正規表現を「リセット」する必要があります。

result.push(re.test('Foo Bar')); 
re.lastIndex = 0; 
result.push(re.test('Foo Bar')); 
// result is now [true, true] 

は、新しいのRegExpオブジェクトを毎回作成するために、読みやすくあり得ることを言って(オーバーヘッドがとして最小限であります正規表現は)とにかくキャッシュされている:各よう

result.push((/Foo B/gi).test(stringA)); 
result.push((/Foo B/gi).test(stringB)); 
32

RegExp.prototype.testは、正規表現lastIndexプロパティを更新します最後のテストが停止した場所からテストが開始されます。それはlastIndexプロパティを更新しませんので、私はString.prototype.matchを使用することをお勧めしたい:

!!'Foo Bar'.match(re); // -> true 
!!'Foo Bar'.match(re); // -> true 

注:!!をブール値に変換し、それが結果を反映したように、ブール値を反転します。

代わりに、あなただけのlastIndexプロパティをリセットできます。

result.push(re.test('Foo Bar')); 
re.lastIndex = 0; 
result.push(re.test('Foo Bar')); 
9

グローバルgフラグを削除すると、あなたの問題を解決します。

var re = new RegExp(query, 'gi'); 

/gフラグを使用して

var re = new RegExp(query, 'i'); 
関連する問題