2008年09月17日
JavaScriptで正規表現を使わずにグローバルな文字列置換
文字列置換のお話。
通常、replace は1回しか置換してくれない。
>>> "aaaa".replace("a", "A") "Aaaa"
何度も置換させるには、正規表現を使うのが手っ取り早い。
>>> "aaaa".replace(/a/g, "A") "AAAA"
置換前の文字列を文字列で受け取ったときには、正規表現オブジェクトを作ればよい。RegExp コンストラクタの第二引数がフラグ。
function myReplaceGlobal(str, before, after){ var reg = new RegExp(before, "g"); return str.replace(reg, after); } myReplaceGlobal("aaaa", "a", "A"); // AAAA
ただ、ドット(.
)が任意の文字にマッチしてしまったりと、弊害もある。
myReplaceGlobal("foo.bar.", ".", "A"); // AAAAAAAA
ドット(.
)は正規表現で任意の文字を表しちゃう。
さぁ、どうしよう、というのが今回のお題。3つの対策を考えてみた。
escape!!
正規表現をエスケープする。とりあえず、ドット(.
)のみ。
function myReplaceGlobal(str, before, after){ var reg = new RegExp(before.replace(/./g, "\\."), "g"); return str.replace(reg, after); } myReplaceGlobal("foo.bar.", ".", "A"); // fooAbarA
ドット(.
)以外にも対応しなきゃいけないし、将来的に JavaScript の正規表現が拡張されたときの対応が大変。
可能な限り避けたい方法。
(追記) コメント欄で教えてもらいました。replace(/([^0-9A-Za-z_])/g, '\\$1'); でエスケープできるとのこと。
while!!
泥臭い方法。
正規表現を使わず、文字列の replace を使う。1回しか置換してくれないので、while で値が変わらなくなるまで回す。
function myReplaceGlobal(str, before, after){ while(str != str.replace(before, after)){ str = str.replace(before, after); } return str; } myReplaceGlobal("foo.bar.", ".", "A"); // fooAbarA
split & join
1行で書けて、ちょっとかっこいい。
function myReplaceGlobal(str, before, after){ return str.split(before).join(after); } myReplaceGlobal("foo.bar.", ".", "A"); // fooAbarA
"foo.bar." を . で split したら ["foo", "bar", ""] になって、それを A で join するので意図した結果が得られる。
まとめ
3つ示した。どれが好き?