LoginSignup
5
4

Emacs の正規表現置換

Last updated at Posted at 2020-04-05

基本的な使い方

Emacs では query-replace-regexp コマンドまたはキー C-M-% で正規表現置換を実行できる。このコマンドを実行すると、下のようなプロンプトが現れる。

Query replace regexp: 

ここで正規表現を入力する。たとえば小文字のアルファベットにマッチする正規表現を入れてみる。

[a-z]+

さらにプロンプトが現れる。

Query replace regexp [a-z]+ with: 

たとえば空白のまま RET を押す。すると、カーソル現在地から下に向かって検索が行われ、マッチした時点で次のようなプロンプトが表示される。

Query replacing regexp [a-z]+ with : (? for help)

ここで y を押すと置換が実行され n を押すとスキップして次のマッチを探す。! を押すと、以降のプロンプトをすべて y で答えたとみなして置換する。

マッチした文字列を再利用する

たとえば下記の文の "" を「」に置き換えることを考えよう。

"hoge"
"fuga"
"moge"

その場合は、下記のような正規表現置換を使うとうまくいく。

input.regexp
"\(.*\)"
output.regexp
「\1」

ここで .* は任意の文字列を表している。
パターンマッチを取るためには \( \) で囲まなければならない。

応用: org-mode のリンクを markdown 形式のリンクにする

  • org でのリンクは [[リンクURL][リンク文字]]
  • markdown でのリンクは [リンク文字](リンクURL)

となっているため互換性がない。org-mode でメモを取っていたのだがサイトに投稿するときはやむなく markdown に変換しなければならない、ということがある。そのような場合に毎回手書きで直すのは大変なので自動化したい。その目的だけなら org-md-export-to-markdown というコマンドが用意されているので、これでよいのだが勉強のために正規表現だけでそれを実現する。

とてもわかりにくいのでまずエスケープなしで考える。org-mode でのリンクは [[リンクURL][リンク文字]] の形式であるから正規表現は下のようになる。

[[(.+)][(.+)]]

マッチさせるだけならカッコ () は不要だが、リンクURL、リンク文字列を抜き出して利用するときに必要なので与えている。これをエスケープすると下記の形式になる。

input.regexp
\[\[\(.+\)\]\[\(.+\)\]\]

カッコで囲まれたパターンにマッチした文字列は Emacs では \1, \2, \3, ... と書けば参照できる。これを使うとmarkdown でのリンクは、下記の通り書ける。

output.regexp
[\2](\1)

query-replace-regexp を手打ちする場合はこれでよいのだが、この正規表現を覚えておくのは不可能なので、lisp の関数を作ってみることにした。その場合は正規表現を文字列として書かなければならないため、バックスラッシュに対してエスケープが必要になる。最終的には、次のような関数を作った。

(defun replace-link-from-org-to-markdown ()
  (interactive)
  (push-mark)
  (goto-char (point-min))
  (replace-regexp "\\[\\[\\(.+\\)\\]\\[\\(.+\\)\\]\\]" "[\\2](\\1)")
  (pop-mark))

これを init.el に書き込んでおけば、関数 replace-org-to-markdown を呼ぶだけでリンクを置換できる。

ついでに他の記号についても置き換えるように関数を拡張して、最終的には下のような実装にした。

(defun replace-org-to-markdown ()
  (interactive)
  (save-excursion
    (let (replacement)
      (setq replacement
            '("^* "                                "# "
              "^** "                               "## "
              "^*** "                              "### "
              "^**** "                             "#### "
              "^***** "                            "##### "
              "#\\+BEGIN_SRC"                      "```"
              "#\\+END_SRC"                        "```"
              "\\[\\[\\(.+\\)\\]\\[\\(.+\\)\\]\\]" "[\\2](\\1)"))
      (while replacement
        (goto-char (point-min))
        (replace-regexp (pop replacement) (pop replacement))))))
5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4