JavaScript:exec( )メソッドについて。

〽️ 恐怖!無限ループ。


こんにちは。「ふ」です。

以前、replaceメソッドを使っての「" "で囲まれた部分のCSSを書き換える」方法を紹介しましたが、今回はその補足説明をさせていただきます。

exec()メソッドについて

execとは、execution(実行)、もしくはexecute(実行する)の意味だと思われます。文字列strと正規表現パターンregExpを用意します。

var str = "りんごみかんりんごみかん"; var regExp = /りんご/;

この文字列においてマッチした値を取り出したいときに

regExp.exec(str);

と記述します。
実行後、マッチした文字列が配列方式で返されます。

このとき、正規表現パターンを宣言した「var regExp」は、lastIndexというプロパティを持っています。最初に宣言した正規表現パターンに

var regExp = /りんご/「「1g」」;

と、グローバルオプションがつけられた場合、「1つ目のマッチは取り出しました、次は〇〇文字目から検索を始めます」の「〇〇」がInt(整数値)で返ってきます。



試してみます。

記述例を示します。exec()の結果は変数resultに代入しましょう。

var str = "りんごみかんりんごみかん"; var regExp = /りんご/g; var result = regExp.exec(str);

resultを出力してみます。

console.log(result); 「「1 ▶︎ ["りんご"]」」

マッチした文字列が配列に収められて返ってきました。
regExpのlastIndexはどうなっているでしょうか。

console.log(regExp.lastIndex); 「「1 ▶︎ 3」」

lastIndexは0から始まるので、「3」は画像のこの部分ですね。strでは「りんご」が2回登場しますが、このことから1回目のexecで取り出されたのは1つ目の「りんご」だったことが分かります。

あと2回やってみます。

「「3//2回目」」 console.log(regExp.exec(str)); 「「1▶︎ ["りんご"]」」 console.log(regExp.lastIndex); 「「1▶︎ 9」」

2つ目の「りんご」が取り出され、lastIndexも更新されました。

「「3//3回目」」 console.log(regExp.exec(str)); 「「1▶︎ null」」 console.log(regExp.lastIndex); 「「1▶︎ 0」」

str内には「りんご」は2つまでしかないので3回目はnullが返ってきます。
lastIndexもリセットされて0になりました。



グループ化。

次にグループ化について紹介します。正規表現パターンの一部を()で囲んでexec()メソッドを実行すると、返される配列の中に()で囲まれた部分が要素として加えられます。

var str = "りんごみかんりんごみかん"; 「「3//「ん」だけ括弧で囲んでグループ化させる」」 var regExp = /り「「1(ん)」」ご/g;

exec()メソッドを実行します。

console.log(regExp.exec(str)); 「「1▶︎ ["りんご","ん"]」」

配列の1つ目はマッチした文字列全体、2つ目にグループ化された部分だけが入りました。
例えば「」に囲まれた部分をマッチさせて、その中身だけを取り出したい場合などには便利な機能ですね。

var str = "りんご「みかん」りんごみかん"; var regExp = /「(.*?)」/g; console.log(regExp.exec(str)); 「「1▶︎ ["「みかん」","みかん"]

「」の中身だけが取り出せました。



恐怖!無限ループ

扨(さて)このexec()メソッド、実際はwhile文を使ってマッチした文字列を順に取り出す、という使い方が多いかと思われます。

while(result = regExp.exec(str)) { 「「3・・・・・・」」 }

しかしこのとき起こりうるのが恐怖の無限ループ。その例を紹介しておきます。




var regExp = /りんご/;

上記のようにグローバルオプション「g」がついていないパターンでexec()メソッドを実行すると、そのlastIndexは更新されることがなく、毎回0にリセットされてしまいます。
ちょっとやってみますか。

var str = "りんごみかんりんごみかん"; 「「3//'g'オプションなし」」 var regExp = /りんご/; 「「3//関数を定義」」 function tamesi() { var result = regExp.exec(str); console.log(regExp.lastIndex); }

lastIndexを出力する関数「tamesi」を何度か実行してみます。

tamesi(); 「「1▶︎ 0」」 tamesi(); 「「1▶︎ 0」」 tamesi(); 「「1▶︎ 0」」

regExpのlastIndexがご覧の通り、何度やっても0にリセットされてしまいます。通常regExpは「g」オプションがついているのでexec()が実行されるたびにlastIndexが更新され、検索がstrの最後まで進むと、while文の条件、

while「「1(result = regExp.exec(str))」」 { ・・・・・・ }

これを抜け出すことができ、メソッドが終了します。
ところが「g」オプションがついていない場合。lastIndexが毎回リセットされるため、常にexec()はテキストの先頭から検索を開始することになります。そのためwhile文の条件を抜け出すことができません。即ち、

💧 無限ループ。

を引き起こすのじゃ。エラーとなってしまいブラウザはロードできません。


もう1つはこれ。

while(result = regExp.exec(str)) { 「「1var regExp = /りんご/g;」」 ・・・・・・ }

while文の内部で正規表現パターンを宣言してしまっています。この場合も、lastIndexがその都度「0」となってしまうため、




💧 無限ループ。



これもあきまへん。
〜while文でexec()メソッドを使う際には注意しましょう。



実はまだありそうなのだ。

ここまでexec()メソッドについて紹介させてもらいました。while文による無限ループは恐ろしいものです。
ところが現在「ふ」はまた別の無限ループ問題を抱えてしまっています。それがregExpのパターンに問題があるのか、while{ }内部での問題なのかまだ解明できていません。解明でき次第、また発信したいと思っています。閲覧ありがとうございました。











「ふ」です。

swift、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。

🐧 twitter 🐧