日々のコンピュータ情報の集積と整理

Dr.ウーパのコンピュータ備忘録

2014年4月20日日曜日

JavaScript:正規表現オブジェクト(RegExp)を使用した時にハマった罠

JavaScriptで文字列を正規表現(正規表現文字列の一部を変数で指定)で置換しようとしたときにハマった罠です。
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)を使用する方法の場合、正規表現は文字列として評価されるため、メタキャラクタに\(エンマーク)が使用されている場合には、\\としてエスケープシーケンス処理にて一つの\として評価されるようにします。

確かに文字列として評価されるので、\を二つ重ねて\\とすればよいということはすぐにわかりますが、正規表現の表記として/~/を使っていて、その表現をそのまま正規表現オブジェクトに用いるとハマってしまう罠でした。

参考文献

javascript:replace()で正規表現に変数を使う場合 - クウネル戦隊ガキュオーン備忘録javascript:replace()で正規表現に変数を使う場合 - クウネル戦隊ガキュオーン備忘録

正規表現の構文正規表現の構文

特殊文字 (JavaScript)特殊文字 (JavaScript)





関連記事

関連記事を読み込み中...

同じラベルの記事を読み込み中...