2021年11月30日 JavaScript (JS Primer) 正規表現
[コラム]RegExp#execメソッド
String#matchAllメソッドは、ES2020で導入されたメソッドです。 それまでは、RegExp#execメソッドというString#matchメソッドによく似た挙動をするメソッドを利用して、String#matchAllメソッド相当の表現を実装していました。
gフラグなし
matchメソッドと同じです。
gフラグあり
const str = "ABC あいう DE えおFG"; const alphabetsPattern = /[a-zA-Z]+/g; // まだ一度も検索していないので、lastIndexは0となり先頭から検索が開始される console.log(alphabetsPattern.lastIndex); // => 0 // gフラグありでも、一回目の結果は同じだが、`lastIndex`プロパティが更新される const result1 = alphabetsPattern.exec(str); console.log(result1[0]); // => "ABC" console.log(alphabetsPattern.lastIndex); // => 3 // 2回目の検索が、`lastIndex`の値のインデックスから開始される const result2 = alphabetsPattern.exec(str); console.log(result2[0]); // => "DE" console.log(alphabetsPattern.lastIndex); // => 10 const result3 = alphabetsPattern.exec(str); console.log(result3[0]); // =>FG console.log(alphabetsPattern.lastIndex); // => 15 const result4 = alphabetsPattern.exec(str); console.log(result4); // => null console.log(alphabetsPattern.lastIndex); // => 0
- 上記の通り、正規表現のgフラグとexecメソッドで検索した場合に、lastIndexプロパティが検索ごとに更新されていくことがわかります。
- result4では検索結果が見つからないため、nullを返し、
lastIndex
プロパティは0にリセットされています。 - マッチする検索結果が見つからなくなった際にnullを返すことを利用してwhile文で反復処理して出力することでmatchAllメソッドと同じような反復処理を実装することも出来ていました。
結論
matchメソッドではgフラグありでは返せなかったindexとinputの値もRegExp#execメソッドでは返してくれますが、Iteratorを扱えるString#matchAllの方が直感的に反復処理を行える点で優れているため、String#matchAllを使える場合はこちらを利用した方が良いでしょう。
特殊文字とRegExp#testメソッドで正規表現の真偽値を取得
Stringメソッドで真偽値を取得したときに学んだものと照らし合わせて書かれていたのが分かりやすかったので引用します。
String#startsWith: /^パターン/.test(文字列) ^ は先頭に一致する特殊文字 String#endsWith: /パターン$/.test(文字列) $ は末尾に一致する特殊文字 String#includes: /パターン/.test(文字列)
console.log(/検索したいパターン/.test(検索対象)) 最低限、この型を覚えておくと良いでしょう。
正規表現と文字列どちらを使うべきか?
次のコードは、/で始まり、/で終わる文字列かを正規表現と文字列のメソッドそれぞれで判定して比較しています。
const str = "/正規表現のような文字列/"; // 正規表現で`/`からはじまり`/`で終わる文字列のパターン const regExpLikePattern = /^\/.*\/$/; // RegExp#testメソッドでパターンにマッチするかを判定 console.log(regExpLikePattern.test(str)); // => true // Stringメソッドで、`/`からはじまり`/`で終わる文字列かを判定する関数 const isRegExpLikeString = (str) => { return str.startsWith("/") && str.endsWith("/"); }; console.log(isRegExpLikeString(str)); // => true
正規表現のパターンそのものを見ても/^\/.*\/$/と分かりづらいため、 可読性が落ちてしまいます。 正規表現を扱う際はコメントや変数名で明示しておいたほうがよいそうです。
Stringメソッドで表現できることはStringメソッドで表現し、 柔軟性や曖昧な検索が必要な場合はコメントとともに正規表現を利用する という方針を推奨します。