ことさら−古都プログラマーの更級日記

京都でお寺を回りながら御朱印集めをしていたり、LoLをしたり試合を見に行ったりしているエンジニアのブログです。技術的なはなしとか日常的なはなし、カメラやLoLや競馬の話も書きます。右メニューに検索やらカテゴリーやらがあるので、見たい記事だけ見てね!

JavaScript の正規表現で new RegExp('[\s\S]', 'gm') して、改行も含めた全ての文字列とマッチさせようとしてもうまくマッチしない

結論

new RegExp('[\s\S]', 'gm')

ではなく

new RegExp('[\\s\\S]', 'gm')

です

f:id:yoshiki_utakata:20180125113828j:plain

JavaScriptでの正規表現の書き方

JavaSctiptの正規表現のドキュメントはこちらです。

developer.mozilla.org

正規表現の書き方は2種類あって

  • /hogehoge/gm
  • new RegExp('hogehoge', 'gm')

いずれかで書きます。ここで注意が必要なのが、例えば改行 \r\n などにマッチさせたい場合は

  • /\r\n/
  • new RegExp('\\r\\n')

それぞれこういうように書くことになります。

正規表現で改行も含めた文字列にマッチする方法

正規表現で有名な . ですが、これは改行にはマッチしません。

.: (この文字は小数点です) 改行文字(\n\r\u2028、あるいは、\u2029)を除いたあらゆる 1 文字にマッチします。 (https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp より)

正規表現のフラグがあり、その中にmというものがあります。

m: 複数行に渡るマッチ。先頭および終端を示す文字 (^ や $) が、複数の行で機能します (すなわち、入力文字列全体の先頭および終端だけでなく各々の行 (\n や \r で区切られる) の先頭および末尾にマッチします)。 (https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp より)

なんとなくmを使うとマッチするような気がしますが、そもそも . の定義の中に改行は入ってないので、 /.+/m のようにしても、. は改行にマッチすることがありません。

qiita.com

そこで登場するのが [\s\S] です

\s : スペース (space)、タブ、改ページ、改行、その他のユニコードでのスペースを含む、単一の空白文字にマッチします。[\f\n\r\t\v​\u00a0\u1680​\u180e\u2000​-\u200a​\u2028\u2029\u202f\u205f​\u3000\ufeff] と同等です。 \S: 空白以外の単一の文字にマッチします。[^\f\n\r\t\v​\u00a0\u1680​\u180e\u2000​-\u200a​\u2028\u2029\u202f\u205f​\u3000\ufeff] と同等です。 (https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp より)

これにより [\s\S] はすべての文字列となるので改行も含めて全ての文字列にマッチします。

os0x.g.hatena.ne.jp

// strの全体がマッチする
str.match(/[\s\S]*/));

文字列結合して正規表現を使いたい場合

こういう関数を定義したいです

function removeTagBlock(tag, html) {
  // <tag>任意の文字列</tag> を除去したい
}

正規表現に文字列結合を使いたい場合は /.../ が使えないので、new RegExp を使う必要があります。

そこで最初はこう書いていましたが。これでは動きません。

removeTagBlock(tag, html) {
  return html.replace(new RegExp('<' + tag + '>[\s\S]*?</' + tag + '>' , 'gim'), '');
}

なぜか!RegExpの第一引数はstringなので\は特別な意味を持ちます。\自体をエスケープしてやる必要があります。正しくはこうです

removeTagBlock(tag, html) {
  return html.replace(new RegExp('<' + tag + '>[\\s\\S]*?</' + tag + '>' , 'gim'), '');
}

改めて結論

めっちゃハマった。\\ 気をつけよう!