JavaScript では、文字列を置換する場合には以下の 2 通りの方法が使用できます。
(1) /~/ を使用する方法
ソースコード:
<script type="text/javascript"> <!-- var name = "田中さん"; document.write("元の名前 : " + name + "<br />"); var new_name = name.replace(/田中/g, "佐藤"); document.write("置換後の名前 : " + new_name + "<br />"); // --> </script>
実行結果:
(2) 正規表現オブジェクト(RegExp)を使用する方法
ソースコード:
<script type="text/javascript"> <!-- var name = "田中さん"; document.write("元の名前 : " + name + "<br />"); var regExp = new RegExp("田中", "g"); var new_name = name.replace(regExp, "佐藤"); document.write("置換後の名前 : " + new_name + "<br />"); // --> </script>
実行結果:
※ この例では意味がないですが、正規表現に g フラグを指定することで、一か所のみの置換ではなく、すべて置換するようにしています。
本題
もし、正規表現の文字列の一部に変数を使用しようとした場合、1番目の「/~/ を使用する方法」では実現できず、2 番目の「正規表現オブジェクト(RegExp)を使用する方法」を用いる必要があります。その時にハマった罠なのですが、「/~/ を使用する方法」と「正規表現オブジェクト(RegExp)を使用する方法」の正規表現文字列は等価だと思っていたので、以下のようにスクリプトを記載しました。
実現したい処理)
“田中 さん” を ”田中 様” に置換したい。→ 「/~/ を使用する方法」では、
var new_name = name.replace(/田中\sさん/g, "田中 様");とすればよい。
\s : タブやスペースなどの空白を意味するメタキャラクタ
なお、”田中” と “さん” は変数によって指定するものとする。
ソースコード:
<script type="text/javascript"><!--
var name = "田中 さん";
document.write("元の名前 : " + name + "<br />");
var text1 = "田中";
var text2 = "さん";
var regExp = new RegExp(text1 + "\s" + text2, "g");
document.write("[" + regExp.toString() + "]");
var new_name = name.replace(regExp, "田中 様");
document.write("置換後の名前 : " + new_name + "<br />");
// -->
</script>
実行結果
元の名前 : 田中 さん置換後の名前 : 田中 さん
あれ!?置換できていない!!
おかしいと思い、regExp の toString() を見てみました。
ソースコード:
<script type="text/javascript"><!--
var text1 = "田中";
var text2 = "さん";
var regExp = new RegExp(text1 + "\s" + text2, "g");
document.write("[" + regExp.toString() + "]");
// -->
</script>
実行結果:
[/田中sさん/g][/田中sさん/g]となっており、空白を意味する \s ではなくただの s になっていました。
つまり、文字列として \s が評価された結果、\(エンマーク)がエスケープシーケンスとして処理され、ただの s になってしまったのでしょう。そのため、正規表現のメタキャラクタとして動作しなかったため、期待した動作にならなかったということです。
文字列として評価された後、正規表現のメタキャラクタとして評価されるには、\\s という用に記載し、\を2つ重ねて\\として、エスケープシーケンス評価後に 1つの\として評価されるようにします。
修正後の結果
ソースコード:
<script type="text/javascript"><!--
var name = "田中 さん";
document.write("元の名前 : " + name + "<br />");
var text1 = "田中";
var text2 = "さん";
var regExp = new RegExp(text1 + "\\s" + text2, "g");
var new_name = name.replace(regExp, "田中 様");
document.write("置換後の名前 : " + new_name + "<br />");
// -->
</script>
実行結果
元の名前 : 田中 さん置換後の名前 : 田中 様
これで期待した結果が得られました。
まとめ
- 正規表現の文字列の一部に変数を使用しようとした場合、正規表現オブジェクト(RegExp)を使用する方法を用います。
- 正規表現オブジェクト(RegExp)を使用する方法の場合、正規表現は文字列として評価されるため、メタキャラクタに\(エンマーク)が使用されている場合には、\\としてエスケープシーケンス処理にて一つの\として評価されるようにします。
確かに文字列として評価されるので、\を二つ重ねて\\とすればよいということはすぐにわかりますが、正規表現の表記として/~/を使っていて、その表現をそのまま正規表現オブジェクトに用いるとハマってしまう罠でした。
コメントを投稿
コメント投稿機能について