【JavaScript】 正規表現リテラルと文字列リテラルで¥(\)の取り扱いの差
更新日:2022/12/15
JavaScriptは、正規表現リテラルと文字列リテラルの二つの方法で正規表現をおこなうことができます。
パターン文字を検索したいとき\でエスケープする必要がありますが、正規表現リテラルと文字列リテラルでは記述方法が異なります。
結論
とりあえず結論から。
正規表現パターンを文字列リテラルから生成する時に、パターン内に \ があるなら \ を二つ重ねる必要があります。
例:
正規表現リテラル: /\^\.*\?/
文字列リテラル: "\\^\\.*\\?"
文字列リテラルでのエスケープ
. や * などのメタ文字を検索するときは、\でエスケープする必要があります。
次のように、正規表現リテラルでのパターンは分かりやすいと思います。
正規表現リテラルでのエスケープ
// 小数点以下を抜き出したい
const reg1 = /.\d*/; // ← . にエスケープなし
console.log( "123.456".match( reg1 ) ); // "123" ←失敗
const reg2 = /\.\d*/; // ← . にエスケープあり
console.log( "123.456".match( reg2 ) ); // ".456" ←成功
しかし、文字列リテラルの場合、同じ記述ではうまくいきません。
文字列リテラルでのエスケープ:失敗例
const reg = new RegExp( "\.\d*" );
console.log( "123.456".match( reg ) ); // "1" ←期待した結果ではない
文字列リテラルでエスケープする場合は、次のように\を二つ入力します。
"\." → "\\."
文字列リテラルでのエスケープ:成功例
const reg = new RegExp( "\\.\\d*" );
console.log( "123.456".match( reg ) ); // ".456"
上記の例では、\dも \\d に書き換えています。
\D , \s なども、\を二つ入力する必要があります。
正規表現リテラル例 | 文字列リテラル |
---|---|
/\d/ | "\\d" |
/\D/ | "\\D" |
/\s/ | "\\s" |
※ブラウザでユーザーが入力した文字や、サーバーとの通信で取得した文字などはそのまま使用できます。
ブラウザで正規表現パターンを入力
<script>
window.addEventListener( "DOMContentLoaded" , ()=> {
const inTag = document.getElementById("in");
inTag.addEventListener("change",()=>{
const reg = new RegExp( inTag.value );
console.log( "abc?123".match( reg ) );
});
});
</script>
<input type="text" id="in" >
上の例で表示される入力欄に \? と入力すると、その下に ? が表示されます。
JavaScriptでエスケープが必要な文字
正規表現で検索対象とするとき、エスケープが必要な文字を挙げてみます。
文字 | エスケープ適用 | 備考 | |
---|---|---|---|
正規表現リテラル | 文字列リテラル | ||
\ | /\\/ | new RegExp("\\\\") | エスケープ文字そのもの |
. | /\./ | new RegExp("\\.") | 改行以外の一文字にマッチ |
* | /\*/ | new RegExp("\\*") | 直前のパターンを0回以上 繰り返すとマッチ |
+ | /\+/ | new RegExp("\\+") | 直前のパターンを1回以上 繰り返すとマッチ |
? | /\?/ | new RegExp("\\?") | 最短マッチ |
[ | /\[/ | new RegExp("\\[") | []内の一文字にマッチ。 ]はエスケープの必要なし |
{ | /\{/ | new RegExp("\\{") | {}内で繰り返し数を指定。 }はエスケープの必要なし |
( ) | /\( \)/ | new RegExp("\\( \\)") | グループまたは後読み先読み。 ( )の両方でエスケープ必要 |
^ | /\^/ | new RegExp("\\^") | 行頭を表す |
$ | /\$/ | new RegExp("\\$") | 行末を表す |
| | /\-/ | new RegExp("\\-") | [ ]内のみエスケープ必要 |
- | /\|/ | new RegExp("\\|") | 条件一致 |
/ | /\// | new RegExp("/") | 正規表現リテラルの開始と終了を表す文字。 文字列リテラルはエスケープの必要なし |
" | /"/ | new RegExp("\"") | 文字列リテラルを表す文字。 "を ' ' で囲む場合 |
' | /'/ | new RegExp('\'`) | 文字列リテラルを表す文字。 'を " " で囲む場合 |
( ) は前後共にエスケープが必要です。
それに対して、[ ] と { } は閉じカッコにエスケープが必要ありません。
しかし、エスケープしても動作します。
文字列リテラルで\が二つ必要な理由
正規表現リテラルでは\が一つで済むのに、なぜ文字列リテラルは二つ必要なのでしょうか。
まずは文字列リテラルの仕組みを知る必要があります。
JavaScriptのシステムがスクリプトコードを読み込む際、文字列リテラルは文字列データに変換されます。
そのとき、\nなどのエスケープ文字を表す文字列は、内部的に一つの文字データに変換されます。
文字列リテラルの変換例
スクリプトコード:
const text = "今日は\n雨"
JavaScriptシステムが文字列に変換:
text : {
0 : "今"
1 : "日"
2: "は"
3: 0x0a (改行コード)
4: "雨"
}
では、"\."や"\?"はどうなるのでしょうか。
こちらは、\が消えて "." や "?" に変換されます。
文字列リテラル例 | 文字列データ |
---|---|
"\." | "." |
"\?" | "?" |
"\*" | "*" |
new RegExp( ) は、変換後の文字を正規表現パターンとして処理します。
つまり、"\."は . という正規表現パターンとして処理されます。
"\?"は ? です。
エスケープは消えているので、適用されないのです。
エスケープとして適用するには、文字列データ内に \ を残す必要があります。
\ を残すには、次表のように \を二つ続けて入力します。
文字列リテラル例 | 文字列データ |
---|---|
"\\." | "\." |
"\\?" | "\?" |
"\\*" | "\*" |
このデータをnew RegExp( )が解釈すると、エスケープが適用されます。
正しいパターンか確認する方法
パターンによっては \ が大量に必要になるので、少し混乱します。
そんな時は、一度 console.log() などで文字列を表示すると、簡単に確認できます。
例えば、郵便番号を検索する正規表現パターンは、正規表現リテラルで次のように表せます。
/\d{3}-\d{4}/
これを文字列リテラルで表現したい時は、次のようにして確認します。
const reg = "\\d{3}-\\d{4}";
console.log( reg ); // \d{3}-\d{4}
結果が正規表現リテラルと同じになったので、正しく変更できています。
ちなみに \ が足りないと、同じ結果になりません。
const reg = "\d{3}-\d{4}";
console.log( reg ); // d{3}-d{4}
例えば入力欄に \d{3}-\d{4} と入力されたら、d{3}-d{4} ではなくて、 \d{3}-\d{4} という文字列を受け取ります。
この記事で解説しているのは、あくまで文字リテラルで記述したケースなので、混同しないようにしましょう。
更新日:2022/12/15
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。