@kyanny's blog

My thoughts, my life. Views/opinions are my own.

re-builder で作った正規表現を query-replace-regexp に渡しても動作しない?

結論

re-builder で作った「文字列」を (insert "XXXX") して出力された「正規表現」を query-replace-regexp に渡せ

メモ

M-x re-builder で正規表現を組み立てるとき、その正規表現は「文字列」として与えなければならない(っぽい)。逆に、 query-replace-regexp に正規表現を与えるときは、「リテラル」として与えなければならない(っぽい)。

以下、結論に至るまでの試行錯誤と考察を箇条書きでメモ。あんまり真面目に検証してません。マニュアルも読んでません。正規表現(特に Emacs の!)に詳しい人のツッコミをお待ちしております。

  • <input type="text" name="emacs" value="lisp" /> を name="emacs" に置換したい、とする
  • re-builder では "^.*\\(name=\"[^\"]+\"\\).*$" と書くとマッチする(し、ハイライトの具合から name="emacs" の部分をキャプチャできてるみたい)
  • "" で囲った中に書くので、これは文字列として正規表現を渡しているのだと思う
  • C-cC-w して query-replace-regexp にそのまま渡しても動かない
  • 囲ってる "" は不要だけどコピペされちゃうようなので(中身だけコピペするべきだよなあ、バグと言えるのでは?)前後の "" を削って渡しても動かない
  • ^.*\(name="[^"]+"\).*$ という正規表現を渡すと動く(ちなみにキャプチャした部分を置換後の文字列中に使いたい場合は \1 とかする)
  • 以上、挙動の観察
  • 以下、自分の考察
  • re-builder のほうでは、 \\ と書いてエスケープのためのバックスラッシュを一つ書いたのと同じ扱いになる(Perl なら qr/\[/ みたいな。メタ文字をエスケープ)
  • Emacs の正規表現では () はエスケープしないといけないので \( と表現しないといけない、がエスケープのためのバックスラッシュを文字列で表現すると \\ となるので、 \\( と書く必要がある(文字列として表現する場合は)
  • query-replace-regexp のほうでは \ はそれ一つでエスケープのためのバックスラッシュの意味をもつので \( と書けばよい(相変わらず () はエスケープする必要がある)
  • 括弧に関してはこれで違いがわかったけど、 \" ってのは?
  • これはたぶん正規表現とは無関係で、 "" で囲った文字列の中にダブルクォートが現れるならばエスケープしないとダメよ、ということ
  • で、その場合はバックスラッシュ一つでエスケープの意味をもつ(\" と書けばよく \\" と書くと誤りということ?わかりづらい!!)
  • 正規表現を文字列として書き下すときのエスケープ文字の書き方と、単なる文字列中のエスケープ文字の書き方が混ざるので、わかりづらくなっているのだと思う
  • マニュアルを読めばきっと書いてある類のことなんだと思う、けどマニュアルの英語を読んでも意味がわからなくて悲しい

余談。 Emacs の正規表現についていままで読んだなかで一番わかりやすかった文献は http://www.mew.org/~kazu/doc/elisp/regexp.html だ。↑に書いた内容も、ぐぐっててこの文章を再発見して読み直したから、多少は整理できて、書き出すことができた(し、無事に正規表現も書けた)。 (insert "") をしなさい、というテクニックは絶対覚えるべきだと思った(覚えてないから↑みたいなことわざわざ書いてるんだけど)。さっきの例でいうと re-builder でつくった "^.*\\(name=\"[^\"]+\"\\).*$" という文字列を (insert "^.*\\(name=\"[^\"]+\"\\).*$") してみると、 query-replace-regexp に渡すべき ^.*\(name="[^"]+"\).*$ という正規表現が出力される。つまり、(結論に戻る)