6時だョ!!全員集合!!

Rails・JavaScrictを中心にアウトプットします。

2021年11月30日 JavaScript (JS Primer) 正規表現

[コラム]RegExp#execメソッド

String#matchAllメソッドは、ES2020で導入されたメソッドです。 それまでは、RegExp#execメソッドというString#matchメソッドによく似た挙動をするメソッドを利用して、String#matchAllメソッド相当の表現を実装していました。

gフラグなし

matchメソッドと同じです。

gフラグあり

  • マッチしない場合は、nullを返す
  • マッチした場合は、マッチした文字列を含んだ特殊な配列を返す
  • 正規表現のgフラグがある場合は、マッチした文字列を含んだ特殊な配列を返し、マッチした末尾のインデックスを正規表現オブジェクトのlastIndexプロパティに記録する
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メソッドで表現し、 柔軟性や曖昧な検索が必要な場合はコメントとともに正規表現を利用する という方針を推奨します。

参考

JSprimer 正規表現