VimでMarkdownのメモを取るときに良い感じにしようとしました
VimでMarkdown形式のメモを取ることが良くあるのですが、
日本語でメモをする際に手元もモニターも見ずにメモっていると
記号が全角になっていたり半角スペースと全角スペースが入り乱れてげんなりします。
そこで、入力するときはとりあえず諦めて入力後にまとめて置換する戦法でこの問題を解決してみました。
やっていることは簡単な正規表現による置換とvimrcの設定だけです。
以下解説が続くので、とりあえず結論だけ知りたい方は下までスキップして下さい。
Vimで置換する
Vimで現在開いているファイルの特定の文字列を置換するにはExコマンドで
:%s/"置換前"/"置換後"/g
とします。
今回は、一々パターンが存在するかどうか確認せずに置換したいので末尾にeを付けて、
:%s/"置換前"/"置換後"/ge
とします。
eを付けると"置換前"に一致するパターンが見つからなくてもエラーが出て処理が止まる事が無くなります。
今回の置換で気をつける点
基本的には素直に
%s/+/+/ge
%s/_/_/ge
.
.
.
のように列挙していけば大丈夫なのですが、「ー」を「-」に置換する際には一工夫必要でした。
%s/ー/-/ge
としてしまうと、
以下のような長音の混じったファイルの場合
ー コンピュータ
ー データ
長音まで置換されてしまい、とても残念な感じになってしまいます。
- コンピュ-タ
- デ-タ
これを回避するため、行の先頭にある「ー」のみ「-」に置換する必要があります。
結論は下のようにすれば期待通りに置換できるのですが、少しややこしいので解説します。
%s/\(^\s*\)ー/\1-/ge
\(
と \)
で囲まれた部分は、ホールドバッファと呼ばれ、後から\1
,\2
,\3
...,\9
と行った形で呼び出すことが出来ます。
ホールドバッファについてはvim (vi) でマッチした文字列の一部を置換 | Mazn.net
が分かりやすいです。
今回はこのホールドバッファに「行の先頭の0個以上の空白」を意味する^\s*
を入れ、後から\1
で呼び出しています。
この処理を行わず、単純に
%s/^\s*ー/-/ge
とした場合
ー コンピュータ
ー データ
ー データ
上のようなテキストを置換する際に
- コンピュータ
- データ
- データ
となってしまい、インデントの空白ごと置換して消し去ってしまい良くないです。
2017/04/27追記
コメントで下のようにも書けると教えて頂きました。ありがとうございます。
%s/^\s*\zsー/-/ge
\zs でマッチの開始地点を設定することで「ー」のみを「-」に置換しています。
:h \zs
でヘルプが引けます。
文字を一気に置換出来るようにvimrcをいじる
関数を定義
ここまできたらvimrcに、専用の関数を定義してExコマンドとして一発で置換出来るようにしたいと思います。
Vim scriptで関数を定義するには
function! 関数名
" うんたらかんたら
endfunction
とします。
また、スクリプトローカルな関数名を定義するには関数名の先頭にs:を付けます。
Vim scriptの文法を知りたい場合は
Vimスクリプト基礎文法最速マスター - 永遠に未完成
が大変参考になります。
今回は以下のような関数を定義しました。
function! s:ChangeCharactersD2H()
%s/ / /ge
%s/!/!/ge
%s/#/#/ge
%s/$/$/ge
%s/%/%/ge
%s/^/^/ge
%s/&/&/ge
%s/*/*/ge
%s/(/(/ge
%s/)/)/ge
%s/\(^\s*\)ー/\1-/ge
%s/_/_/ge
%s/+/+/ge
%s/=/=/ge
%s/{/{/ge
%s/}/}/ge
%s/|/|/ge
%s/\/\\/ge
%s/〜/~/ge
%s/`/`/ge
%s/:/:/ge
%s/;/;/ge
%s/,/,/ge
%s/././ge
%s/</</ge
%s/>/>/ge
%s/’/'/ge
%s/”/"/ge
%s/0/0/ge
%s/1/1/ge
%s/2/2/ge
%s/3/3/ge
%s/4/4/ge
%s/5/5/ge
%s/6/6/ge
%s/7/7/ge
%s/8/8/ge
%s/9/9/ge
endfunction
2017/04/27追記その2
%s/0/0/ge
%s/1/1/ge
%s/2/2/ge
%s/3/3/ge
%s/4/4/ge
%s/5/5/ge
%s/6/6/ge
%s/7/7/ge
%s/8/8/ge
%s/9/9/ge
の部分は
%s/[0-9]/\=nr2char(char2nr(submatch(0)) - char2nr('0') + char2nr('0'))/ge
としても同様に置換出来ます。
ぱっと見何をしているのか分かりづらいので以下で解説していきます。
[0-9]
この部分は単純に全角の「0」〜「9」の数に一致する正規表現です。
\=
置換の中で\=
で始まる時は、式で評価されます。ヘルプは:h :s\=
で引けます。
nr2char()
これはASCIIコードを10進数で渡すと対応した文字を返してくれる式です。ヘルプは:h nr2char()
で引けます。
char2nr()
これは先ほどのnr2char()
とは逆に文字からASCIIコードを返してくれます。ヘルプは:h char2nr()
で引けます。
submatch(0)
これは置換でマッチしたテキスト全体を返してくれます。ヘルプは:h submatch()
で引けます。
ここまで分かれば後は余裕です。
例として「5」がマッチして「5」に置換される場合を見てみると、
- 正規表現[0-9]に「5」がマッチする
- submatch(0)によって先ほどマッチした「5」が返される
- 「5」が
char2nr(submatch(0))
でASCIIコードに変換されて「65301」になる - 「65301」から、「0」が
char2nr('0')
でASCIIコードに変換された「65296」が引かれる(「5」になる) - 上で出た「5」に、「0」〜「9」の中で最もASCIIコード順で最初にある「0」を
char2nr('0')
で変換した「48」を足す(「53」になる) - 上で出た「53」をnr2char()で文字に戻すと「5」が「5」に置換される
となります。
この方法は、「あ」〜「ん」までを全てカタカナの「ア」〜「ン」に置換する等の場合にも使えます。便利ですね。
ちなみに
%s/[あ-ん]/\=nr2char(char2nr(submatch(0)) - char2nr('あ') + char2nr('ア'))/ge
となります。
この追記も@h_east さんに教えて頂きました。ありがとうございます。
追記終わり
関数をコマンドにする
command! コマンド名 call 関数名
でコマンドが定義できます。
コマンド定義について詳しく知りたい場合は以下の記事が参考になります。
vim-jp » Hack #158: ユーザコマンドを定義する
今回の場合は
command! ChangeCharactersD2Ha call s:ChangeCharactersD2H()
としました。
これでExコマンドで置換が実行でいるはずなので
後は、vimrcを保存して動作確認をしてみて期待通りに動けば完成です。
また、今回は自分の環境で必要な物だけ置換しましたが、単純に半角全角の置換をするだけの場合は、
Kaoriya版のGvimにデフォルトでインストールされているhz_ja.vimの機能の方が便利です。
最後に
Vimの置換で色々出来ることは多いと思います。
私もまだ初心者なので今回のやり方が100%正しいとは限りません。
もしも、「他にもこんなやり方あるよ」、だとか「こっちのほうが楽だぞ」等
ありましたらコメントいただけるとありがたいです。
また、Vimの置換についてもっとちゃんと知りたい場合はVimで
:h :s
とやってヘルプを引くと良いと思います。
Vimのヘルプはとても充実しているので是非調べて見て下さい。