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
null
のslice
ってメソッドは呼べないよー、ってことです。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]
なのかというと、それぞれ「英数字と英数字以外」、「空白文字と空白文字以外」という意味あいになり、結果的に全ての文字にマッチするからです。
(正規表現はメタ文字を大文字にすると意味が反対になることが多いです)