正規表現の置換文字列でLisp式を使う
この記事はEmacs Advent Calendar 2020の24日目の記事です。空いているともったいないので、12/26に急遽参加しました^^;;;
はじめに
Emacsの特長のひとつが、パワフルな正規表現置換。インクリメンタル置換のquery-replace-regexp
(標準で、C-M-%
にキーバインド)や一括置換のreplace-regexp
は、Emacsでもっとも多くの人々に愛用されている関数の一つではないでしょうか。
そんな正規表現置換をさらに便利にするのが置換する文字列の一部でLisp式を使う機能です。Emacsのマニュアルにも書いてあるように、置換する文字列の中に\,
を記述するとその後の()
にLisp式を記述できます。実例があった方がわかりやすいと思うので、サンプルとして次のCSV形式データを用意しました。
名前,名前のローマ字表記,住所,電話番号,メールアドレス
竈門 炭治郎,tanjiro kamado,東京都西多摩郡奥多摩町日原65536,070-1234-xxxx,tanjiro@example.com
竈門 禰豆子,nezuko kamado,東京都西多摩郡奥多摩町日原65536,080-6543-xxxx,nezuko@example.com
我妻 善逸,zenitsu agatsuma,東京都新宿区箪笥町8192,080-3598-xxxx,zenitsu@example.com
嘴平 伊之助,inosuke hashibira,東京都西多摩郡檜原村海澤32678,080-9234-xxxx,inosuke@example.com
不死川 玄弥,genya shinazugawa,東京都中央区京橋4096-8192,090-2234-xxxx,genya@example.com
栗花落 カナヲ,kanao tuyuri,東京都墨田区向島2048-32-8192,090-2234-xxxx,kanawo@example.com
このサンプルから、次のような置換をします。
- 「名前のローマ字表記」列で、単語(名字と名前)の先頭を大文字にする。「メールアドレス」列は元のままにする
- 「住所」列をすべて全角文字にする。ほかの列はそのままにする
- データ行の先頭に1からはじまる連番を挿入する
文字列の一部のだけアルファベット単語の先頭を大文字にする
Emacsでは、指定した範囲の全体でアルファベット単語の先頭を大文字にするためには、capitalize-region
関数を使います。すべてのアルファベットを大文字にするupcase-region
(C-x C-u
)や小文字にするdowncase-region
(C-x C-l
)とともに覚えておくと便利でしょう。しかし今回のサンプルでは、「メールアドレス」列も変換されてしまうため、capitalize-region
関数をそのまま使うことはできません。
代わりに、query-replace-regexp
やreplace-regexp
で、置換前と置換後の文字列を次のように指定すれば、「名前のローマ字表記」列だけ単語の先頭を大文字にできます。この場合、「メールアドレス」列はもちろん元のままです。サンプル全体を選択してから次の文字列で置換します。
\(.+?\),\(.+?\),\(.+?\),\(.+?\),\(.+\)
\1,\,(capitalize \2),\3,\4,\5
置換後のサンプルデータは次のようになります。
名前,名前のローマ字表記,住所,電話番号,メールアドレス
竈門炭治郎,Tanjiro Kamado,東京都西多摩郡奥多摩町日原65536,070-1234-xxxx,tanjiro@example.com
竈門禰豆子,Nezuko Kamado,東京都西多摩郡奥多摩町日原65536,080-6543-xxxx,nezuko@example.com
我妻善逸,Zenitsu Agatsuma,東京都新宿区箪笥町8192,080-3598-xxxx,zenitsu@example.com
嘴平伊之助,Inosuke Hashibira,東京都西多摩郡檜原村海澤32678,080-9234-xxxx,inosuke@example.com
不死川玄弥,Genya Shinazugawa,東京都中央区京橋4096-8192,090-2234-xxxx,genya@example.com
栗花落カナヲ,Kanao Tuyuri,東京都墨田区向島2048-32-8192,090-2234-xxxx,kanawo@example.com
少し説明しますと、\(.+?\),\(.+?\),\(.+?\),\(.+?\),\(.+\)
のような指定の仕方はコンマ区切りデータを置換するときの常套手段。最後以外の列データは\(.+?\)
で最後の列データだけ「?」のない\(.+\)
で表します。このうち、2列目のデータに対してだけ\,
でLisp式を適用し、capitalize
関数で単語の先頭を大文字に変換するよう指定しています。
文字列の一部のだけ全角文字にする
アルファベットの大文字小文字の変換や単語の先頭を大文字にする作業は、日本ではあまり必要ないかもしれません。代わりに求められるのが、全角や半角の変換作業。住所欄への入力が全角でないと叱られるWebサービスはストレスフルですね。全角半角変換のようなくだらない作業をなんで人様に押し付けるんだろう^^;;; というわけで、「住所」列だけをEmacsを使って全角に変換しましょう。Emacsでは半角文字を全角文字に変換する関数としてjapanese-zenkaku
が用意されています。サンプル全体を選択してから次の文字列で置換します。
\(.+?\),\(.+?\),\(.+?\),\(.+?\),\(.+\)
\1,\2,\,(japanese-zenkaku \3),\4,\5
置換後のサンプルデータは次のようになります。
名前,名前のローマ字表記,住所,電話番号,メールアドレス
竈門炭治郎,Tanjiro Kamado,東京都西多摩郡奥多摩町日原65536,070-1234-xxxx,tanjiro@example.com
竈門禰豆子,Nezuko Kamado,東京都西多摩郡奥多摩町日原65536,080-6543-xxxx,nezuko@example.com
我妻善逸,Zenitsu Agatsuma,東京都新宿区箪笥町8192,080-3598-xxxx,zenitsu@example.com
嘴平伊之助,Inosuke Hashibira,東京都西多摩郡檜原村海澤32678,080-9234-xxxx,inosuke@example.com
不死川玄弥,Genya Shinazugawa,東京都中央区京橋4096−8192,090-2234-xxxx,genya@example.com
栗花落カナヲ,Kanao Tuyuri,東京都墨田区向島2048−32−8192,090-2234-xxxx,kanawo@example.com
連番を挿入する
Emacsでは、データに連番を挿入するためにrectangle-number-lines(C-x r N)が使えます。でも、キーバインドも関数の使い方もなかなか覚えられないし、思った通りに連番が入れられないことも多い…というわけで、置換機能を使って連番を入れるのが便利な場合も多いと私は思っています。
Emacsの正規表現置換では置換後の文字列に\#
を使えば置換された件数を出力できるので、これで連番を入れられます。ところが、連番はたいてい1, 2, 3, ...と1からはじめたいのに、\#
は0, 1, 2, ...と0からはじまってしまいます。でも、そういう悩みも\,
を使えばたちまち解決します。ちょっと変わった名前の1+
という関数をいっしょに使います。サンプルのデータ部分である2行目以降を選択してから次の文字列で置換します。
^.+$
\,(1+ \#),\&
名前,名前のローマ字表記,住所,電話番号,メールアドレス
1,竈門炭治郎,Tanjiro Kamado,東京都西多摩郡奥多摩町日原65536,070-1234-xxxx,tanjiro@example.com
2,竈門禰豆子,Nezuko Kamado,東京都西多摩郡奥多摩町日原65536,080-6543-xxxx,nezuko@example.com
3,我妻善逸,Zenitsu Agatsuma,東京都新宿区箪笥町8192,080-3598-xxxx,zenitsu@example.com
4,嘴平伊之助,Inosuke Hashibira,東京都西多摩郡檜原村海澤32678,080-9234-xxxx,inosuke@example.com
5,不死川玄弥,Genya Shinazugawa,東京都中央区京橋4096−8192,090-2234-xxxx,genya@example.com
6,栗花落カナヲ,Kanao Tuyuri,東京都墨田区向島2048−32−8192,090-2234-xxxx,kanawo@example.com
まとめと続編予告(?)
というわけで、Emacsの正規表現置換では、置換後の文字列の中に\,
を記述することでLisp式を入れることができます。この機能により、正規表現置換をさらに便利に使えるのでぜひ試してください。
なお、正規表現置換でのLisp式では、\1
, \2
, ...のようなグループにされた部分にマッチする文字列で数値を取得して計算することもできます。その機能を使えばEmacsの正規表現置換はさらにパワフルで便利になるのですが…長くなるのでその話はまた別にします。それでは、クリスマスは過ぎてしまいましたけどよい年末年始を!