目的
- JavaScriptとPHPの文字列置換でどうして$1にヒットした文字列が入っているのかわからなかったので調べてみた
前提情報
- JavaScriptは下記の方法で確認した
- PHPは下記のサイトで確認した。
- 下記の内容を実装中に$1になぜヒット内容が入っているか気になった。(本来良くないことだが$1にすればヒットした内容が格納されているということを一つ覚えで実装してしまった。)
確認
-
Chromeの検証モードのコンソールでで下記を実行してみる。
/(https?:\/\/[^\s]*)/g.exec('https://qiita.com/miriwo は筆者のQiitaのホームです');
-
下記の出力が得られた。
画像
-
キー0とキー1にヒットした文字列「https://qiita.com/miriwo」が格納されている。
-
正規表現部分を下記のように修正して実行してみる。
/https?:\/\/[^\s]*/g.exec('https://qiita.com/miriwo は筆者のQiitaのホームです');
-
下記の出力が得られた。
-
キー0にのみヒットした文字列
https://qiita.com/miriwo
が格納されている。 -
どうやら$1などは正規表現部分のいくつめの括弧でマッチしたものかを表しているようである。
-
下記のリンクの「引数としての文字列指定」の「$n」の記載にそれっぽいことが書かれている。
-
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/replace
パターン 挿入 $n n は 100 未満の正の整数です。第一引数が RegExp オブジェクトだった場合に n 番目の括弧でキャプチャされた文字列を挿入します。1, 2, ... でインデックスされることに注意してください。
-
-
$2を作り出すために意図的に下記を実行してみる。ドキュメントの記載どおりなら$2はキーとしては存在するが、検索条件を指定していないので何も値が格納されないはずである。
/(https?:\/\/[^\s]*)()/g.exec('https://qiita.com/miriwo は筆者のQiitaのホームです');
-
予想通り下記のように出力された$2はキーとして存在しているが値は格納されていない。
-
では$0は何を指しているのかを確認するために下記を実行してみた
/(https?)(:\/\/[^\s]*)/g.exec('https://qiita.com/miriwo は筆者のQiitaのホームです');
-
下記のような出力が得られた。下記の結果から$0は括弧に関係なく正規表現部分全体でヒットした文字列が格納されているものであると言える。
-
個人のブログではあるがまとめてくださっている方がいた。「$0 は正規表現にマッチした文字列全体を表し、$1, $2, $3 は、それぞれ1番目、2番目、3番目の左括弧で囲まれた部分のパターンに一致した部分文字列を表します。」と書かれているのでおそらく自分の見解は間違えていないように思える。
付録
-
自分がよく使うPHPでは正規表現を使った置換はどのように行われるのかが気になったので公式ドキュメントを呼んでみた。
-
PHPで正規表現を用いた文字列置換を行うときは
preg_replace()
メソッドを使用するようである。 -
下記のように当該メソッドを使用する。
preg_replace(検索条件文字列, 置換後の文字列, 置換対象文字列);
-
「検索条件文字列」は正規表現を用いて記載するものでありJavaScriptと同じようにデリミタでくくって記載する。
-
そして検索にヒットしたものが入ってくるキーも$0や$1などJavaScriptと一緒のようだった。公式ドキュメントのリンクを下記に記載し抜粋したもの載せる。(PHPのドキュメントのほうがわかりやすく書いてくれている気がした。先にこっちを読めばよかった。)
-
https://www.php.net/manual/ja/function.preg-replace.php
replacement では、 \n 形式または $n 形式で参照を指定することができます。 後者の形式の方が好ましい形式です。各参照は、n 番目のキャプチャ用サブパターンにマッチしたテキストにより置換されます。 n は 0 から 99 までとすることができ、 \0 または $0 は パターン全体にマッチするテキストを参照します。キャプチャ用サブパターンの番号 については、その左括弧が左から右に(1から)カウントされます。 string に含まれるバックスラッシュリテラルは、エスケープが必須であることに注意して下さい。
-
-
試しに下記を実行してURL部分が検索にヒットするか確認してみた。
echo preg_replace('/(https?:\/\/[^\s]*)/', '<a href="$1">$1</a>', 'https://qiita.com/miriwo は筆者のQiitaのホームです');
-
下記のように出力されたので問題無く置換できているようである。
<a href="https://qiita.com/miriwo">https://qiita.com/miriwo</a> は筆者のQiitaのホームです
-
ヒットした文字列が格納されているキーがJavaScriptのものと同じであるのか確認してみる。下記を正規表現部分と置換後の文字列をいじって下記を実行してみる。
echo preg_replace('/(https?)(:\/\/[^\s]*)()/', '$0 - $1 - $2 - $3 ', 'https://qiita.com/miriwo は筆者のQiitaのホームです');
-
下記のように出力された。1つ目のハイフンまでが$0であり、すべての正規表現にヒットしたのでURLがフルで表示されている。1つ目から2つ目のハイフンまでが$1であり1つ目の括弧のヒット部分が表示されている。2つ目から3つ目のハイフンまでが$2であり2つ目の括弧のヒット部分が表示されている。最後のハイフンのあとは$3であり3つ目の括弧のヒット部分が表示されるが括弧の中に何も記載されておらず何もヒットしていないので何も出力されていない。
https://qiita.com/miriwo - https - ://qiita.com/miriwo - は筆者のQiitaのホームです
まとめ
- JavaScriptでもPHPでも正規表現を使った文字列置換の$1とか$2とかは左から数えた正規表現の括弧の数にリンクしており、それぞれ$1には1つ目の括弧の正規表現にヒットした文字列が、$2には2つ目の括弧の正規表現にヒットした文字列が格納れている。$0だけは少し特殊で括弧に関係なくデリミタで区切られた正規表現すべてを通してヒットした文字列が格納される。