LoginSignup
11
11

More than 5 years have passed since last update.

正規表現の`.`でハマった話

Last updated at Posted at 2013-05-26

JavaScriptの正規表現には(というかどの正規表現にも).というメタ文字があります。これは大抵「任意の一文字にマッチ」と説明されていて、僕も今までそういうものだと思っていました。
ですが、どうも違ったみたいです。

こんなコードを書いたとします。

var
c_src = 'int main(){\n printf("Hello, World");\n}';
/*
int main(){
 printf("Hello, World");
}
*/

console.log(c_src.match(/^(\w+)\s+(.*)$/).slice(1));

C言語のプログラムになっている文字列から、返り値となる部分と引数以降を取り出すイメージです(もちろん実際にそんなことしようと思ったらもっとちゃんと構文解析しなければいけませんが…)。

このプログラムの結果として期待するのは、

['int', 'main(){\n printf("Hello, World");\n}']

という出力です。

しかし実際に実行してみるとどうなるでしょうか?

こうなるはずです。

TypeError: Cannot call method 'slice' of null

nullsliceってメソッドは呼べないよー、ってことです。Java風にいうとNullPointerExceptionです。ぬるぽがっ!

なぜこうなるのでしょうか?

JavacriptのString#matchメソッドは正規表現のマッチに失敗した場合nullを返します。
つまり先ほどのエラーは、あの正規表現ではあの文字列に対してマッチしないということになります。

どうしたものか…。
ふと、mというフラグが存在することを思い出して、もしやとつけてみました。

console.log(c_src.match(/^(\w+)\s+(.*)$/m).slice(1));

実行してみます。

['int', 'main(){']

今度はマッチしました!
が、期待する出力と違います。

なぜだ…。

そろそろ訳が分からなくなってきたのでMDNを見てみます。(※強調は編集者による)

. (小数点)__ 改行文字以外__のどの 1 文字にもマッチします。
例えば /.n/"nay, an apple is on the tree"'an''on' にはマッチしますが、'nay' にはマッチしません。

改行文字以外!!
な、なんだってえぇ!

なぜわざわざ改行を省くんだ…。

しかし、この仕様はRubyとかでもあるみたいです。

ひとまずこんな風に書き換えて対処しました。

console.log(c_src.match(/^(\w+)\s+((?:.|\n)*)$/).slice(1));

.の部分を(?:.|\n)と書き換えています。[訂正:2013/05/27]不要な部分にmフラグを立てていたものを消しました。

もしかしたら(?:.|\n|\r)にしたほうがいいこともあるかもしれません。

もっとうまい方法があったら教えていただけると嬉しいです。

[追記:2013/05/27]

コメントで @chick307 様、 @ktty1220 様に教えて頂きました。このようにすればいいみたいです。

//こう
console.log(c_src.match(/^(\w+)\s+([\s\S]*)$/).slice(1));

//もしくはこう
console.log(c_src.match(/^(\w+)\s+([\w\W]*)$/

なぜ[\w\W][\s\S]なのかというと、それぞれ「英数字と英数字以外」、「空白文字と空白文字以外」という意味あいになり、結果的に全ての文字にマッチするからです。
(正規表現はメタ文字を大文字にすると意味が反対になることが多いです)

11
11
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11