正規表現のグローバルフラグと文字列を全て捜査する話
March 6, 2021
やりたかったこと #
文字列内で複数回出現するパターンを順番に処理していきたいという場面があった。
具体的には例えば以下の HTML 文字列があったとして、正規表現を使って各 a タグの href 属性を集める。
<div>
<a href="hoge">text of hoge</a>
<a href="foo">text of foo</a>
<a href="bar">text of bar</a>
</div>
最初に試してうまく出来なかったやり方 #
普通に RegExp#test()
でキャプチャしてた。
let regexp = /<a .*?href=([^ >]+).*?>/;
if (regexp.test(htmlString)) {
let href = RegExp.$1;
console.log(href);
}
最初にマッチするパターンはこれで抽出できる。が、2個目以降のマッチするパターンどうやって出すんだっけ?ってなる。
グローバルフラグをつけて while ループ #
RegExp.prototype.global - JavaScript | MDN に書いてある。
その正規表現が文字列の中で一致する可能性がある場所すべてについてテストを行うことを示します。
正規表現オブジェクトがステートフルになって前回マッチした位置を保持し、次の実行で前回マッチした位置以降に対してパターンマッチをしてくれるというもの。
let htmlString = `<div>
<a href="hoge">text of hoge</a>
<a href="foo">text of foo</a>
<a href="bar">text of bar</a>
</div>`;
let regexp = /<a .*?href=([^ >]+).*?>/g;
while (regexp.test(htmlString)) {
let href = RegExp.$1;
let lastIndex = regexp.lastIndex;
console.log(href, lastIndex);
}
"hoge" 23
"foo" 56
"bar" 88
余談: DOM で評価すれば早くね? #
そーゆー話もある。
$(`<div>
<a href="hoge">text of hoge</a>
<a href="foo">text of foo</a>
<a href="bar">text of bar</a>
</div>`).find('a').each(function () {
console.log(this.href)
})
時と場合で使い分けましょう(SAX Parser のようにストリーム処理したい場合は正規表現ですよね!)