サイトマップ

vi 高度な編集

高度な編集

 ここでは、正規表現を使った検索及び文字列の置き換えについて説明します。

正規表現

 正規表現(Regular Expressions)とは、文字の組み合わせを表現するために用いられるパターンです。vi では、検索と置き換えで使用できます。

 正規表現は、一冊の本で論じられるくらいに複雑なものなので、ここで全てを説明することはできません。私がよく使うパターンを説明します。

特殊文字

 文字列を表現するパターンには、通常の文字が使えますが、一部の文字は、メタ文字として特別な意味があります。メタ文字の中には、出現する場所によって通常の文字となったり、メタ文字となったりするので注意が必要です。

 他にもありますが、このページで説明する特殊文字は以下です。

特殊文字意味
.任意の 1 文字
^行頭
$行末
[候補の始まり
]候補の終わり
*前の文字の 0 回以上の繰り返し
\+前の文字の 1 回以上の繰り返し
\|OR
\(グループの始まり
\)グループの終わり

+|() は、正規表現ではメタ文字ですが、vim では通常文字として扱われます。メタ文字として機能させるためには前に '\'(バックスラッシュ)を付ける必要があります。また、メタ文字である .*^$[] を通常の文字として扱う場合は逆に '\' でエスケープします。

正規表現のメタ文字はこれだけではありません。例えば、繰り返しの回数を指定するメタ文字もあります。興味があれば自分で調べてみてください。

正規表現の例

 例を使って説明します。次のようなテキストで、どの様にマッチするかを説明します。

リンク<br> <ul class="reference"> <li><a class="button" href="reference1.html">参考1</a></li> <li><a class="button" href="reference2.html">参考2</a></li> <li><a class="button" href="reference3.html">参考3</a></li> <li><a class="button" href="https://Example.com/">参考4</a></li> </ul>

全ての文字とその繰り返し

 .(ピリオド)は、任意の 1 文字を表します。

 *(アスタリスク)は、直前の文字の 0 回以上の繰り返しを表します。

 いま、このテキスト中の " (ダブルクォート)で囲まれた文字列を表す正規表現を考えます。

".*"

 " の間は任意の 1 文字の 0 回以上の繰り返しという意味です。

 一見、これは正しいように思えますが、結果は次のようになります。

リンク<br> <ul class="reference"> <li><a class="button" href="reference1.html">参考1</a></li> <li><a class="button" href="reference2.html">参考2</a></li> <li><a class="button" href="reference3.html">参考3</a></li> <li><a class="button" href="https://Example.com/">参考4</a></li> </ul>

ul タグの class="~" はきちんとマッチしますが、a タグは href 属性まで含んでいます。

 これは、正規表現では最長一致が標準だからです。" の間の文字を長くマッチさせるため、" があっても更にマッチさせようとします。

最長一致の反対の最短一致でのマッチングも可能です。vim では、* の代わりに \{-} と書くと最短一致になるそうですが、正規表現の記法とかけ離れているので私は使っていません。

文字候補

 [] は、文字の候補を表します。

 [abc] は、a か b か c の文字を表します。また、範囲を表すこともできます。[a-z] はアルファベットの小文字を表します。

 これを使って、"~" を表現すると次のようになります。

"[a-z]*"

 これは、アルファベットの小文字の 0 回以上の繰り返しを意味します。

リンク<br> <ul class="reference"> <li><a class="button" href="reference1.html">参考1</a></li> <li><a class="button" href="reference2.html">参考2</a></li> <li><a class="button" href="reference3.html">参考3</a></li> <li><a class="button" href="https://Example.com/">参考4</a></li> </ul>

 class の "~" はマッチしましたが、href の "~" はマッチしません。そうです reference1.html ですから数字があるのでマッチしません。

 [] の中に数字も含めたらどうでしょう。

"[a-z0-9]*"

 このような表現も可能です。これなら結果は次のようになります。

リンク<br> <ul class="reference"> <li><a class="button" href="reference1.html">参考1</a></li> <li><a class="button" href="reference2.html">参考2</a></li> <li><a class="button" href="reference3.html">参考3</a></li> <li><a class="button" href="https://Example.com/">参考4</a></li> </ul>

 今度は Example.com が駄目です。大文字も含めて [a-zA-Z0-9]* と表現できます。いやいや、https:// には記号も入っています。href では日本語の可能性もあります。きりがありません。全部候補に書くのは無理があるでしょう。

 [] の候補文字の先頭に ^(キャレット)があると、候補以外の文字という意味になります。つまり、今回は " 以外の文字を使えばいいのです。

"[^"]*"

 これで、" 内の文字は " 以外の文字の 0 回以上の繰り返しという正規表現になります。

リンク<br> <ul class="reference"> <li><a class="button" href="reference1.html">参考1</a></li> <li><a class="button" href="reference2.html">参考2</a></li> <li><a class="button" href="reference3.html">参考3</a></li> <li><a class="button" href="https://Example.com/">参考4</a></li> </ul>

文字候補で範囲を使う場合は、半角文字だけにしておいたほうがいいでしょう。日本語で範囲を使うと、思わぬ文字がマッチしてしまいます。

行頭や行末

 正規表現の最初に ^(キャレット)を書くと行頭、最後に $ を書くと行末を表します。それ以外の場所では通常の文字として扱われます。

 行頭の < は次のような正規表現になります。

^<

リンク<br> <ul class="reference"> <li><a class="button" href="reference1.html">参考1</a></li> <li><a class="button" href="reference2.html">参考2</a></li> <li><a class="button" href="reference3.html">参考3</a></li> <li><a class="button" href="https://Example.com/">参考4</a></li> </ul>

 行末の > は次のような正規表現になります。

>$

リンク<br> <ul class="reference"> <li><a class="button" href="reference1.html">参考1</a></li> <li><a class="button" href="reference2.html">参考2</a></li> <li><a class="button" href="reference3.html">参考3</a></li> <li><a class="button" href="https://Example.com/">参考4</a></li> </ul>

グループ

 \( \) でグループを作ります。検索時には、\| (OR) と組み合わせて、文字列置き換え時には単独でも使います。

 例えば、class="~" と href="~" をまとめる場合、次のようにします。

\(class | href\)="[^"]*"

 この意味は、class または href に続く = " で囲まれている文字列を表します。

リンク<br> <ul class="reference"> <li><a class="button" href="reference1.html">参考1</a></li> <li><a class="button" href="reference2.html">参考2</a></li> <li><a class="button" href="reference3.html">参考3</a></li> <li><a class="button" href="https://Example.com/">参考4</a></li> </ul>

検索で正規表現を使う

 検索で正規表現を使った例を次に示します。

コマンド意味マッチする文字列の例
/<[^>]*>ENTER<>で囲まれた文字列<a href="index.html">
<br>
/[0-9][0-9][0-9][0-9].[0-9][0-9].[0-9[0-9]ENTER西暦の日付2021/08/29
2021-08-29

置き換えで正規表現を使う

 s コマンドでも正規表現が使えます。例えば、特定の文字列を全て同じ文字列に変える。あるいは削除するなどがあります。

コマンド意味
:%:s/"[^"]*"/"string"/gENTER" で囲まれた内容を全てstring に置き換える
:%:s/<[^>]*>//gENTERタグを全て削除する

 文字列の置き換えで正規表現を使うと置き換え元の文字列の必要な部分を利用できます。例えば、次のような日付のテキストがあったとします。

2021/03/24 2021.04.08 2021 04 25 2021/05/16

これを次のように修正したいとします。

西暦2021年03月24日 西暦2021年04月08日 西暦2021年04月25日 西暦2021年05月16日

 これを一度に置き換えするには、グループを使います。年、月、日をそれぞれグループにします。

\([0-9][0-9][0-9][0-9]\).\([0-9][0-9]\).\([0-9][0-9]\)

 日付の区切り文字は、.(ピリオド)、 /(スラッシュ)、 (空白)なので、正規表現では任意の 1 文字を表す . にします。

 置き換えで、置き換え文字列をグループ化すると、グループ化した内容を置き換え後の文字列に使用することができます。置き換え後の文字列で、\1 は、 1 番目のグループの内容を表します。\2 は 2 番目のグループの内容、\3 は 3 番目のグループの内容になります。

 これを利用すると、置き換えコマンドは次のようになります。

:%s/\([0-9][0-9][0-9][0-9]\).\([0-9][0-9]\).\([0-9][0-9]\)/西暦\1年\2月\3日/gENTER

 もし、テキスト内の数字が全て日付であれば、次のように書くこともできます。

:%s/\([0-9]\+\).\([0-9]\+\).\([0-9]\+\)/西暦\1年\2月\3日/gENTER

私は、\+ は正規表現の表記と違っているので、ほとんど使いません(使おうとすると \ を忘れるのでうまくマッチしません。)。次のように * の前をもう一つ増やして 1 文字以上の繰り返しを表すことができます。[0-9][0-9]*正規表現が長くなってしまいますが、\+ を使わなくても困りません。

グローバルコマンド

 パターンにマッチする行に対して指定したコマンドを実行することが出来るグローバルコマンドについて説明します。パターンに正規表現が使えます。

:[範囲]g/パターン/コマンド[パラメータ]

 範囲は、ed コマンド の対象とほぼ同じですが、省略した場合、全ての行が範囲になります

 範囲で指定された行でパターンにマッチする行に対してコマンドを実行します。

 コマンドがパラメータを必要とする場合は、パラメータを書きます。

 私がよく使うのは、空行(改行だけの行)の削除です。コマンドは次のようになります。

:g/^$/dENTER

 パターン(^$, 改行のみ)にマッチする行に対して、d コマンド(削除)を実行します。パターンにマッチする行が多いと、このコマンドはありがたいです。

 空白文字も含んでいる行も削除する場合は、次のようにすればいいです。

:g/^[   ]*$/dENTER

 [] 内は、半角空白、全角空白、タブ(メタ文字である \t と表現してもいい)です。

パターンの区切り文字の '/' は s コマンド と同様に変えることができます。


 vi には、まだまだ便利なコマンドがあります。時間があればマニュアルを読むことをおすすめします。自分に合ったコマンドが見つかると思います。

 また、正規表現もほんの一部しか説明していません。表現が面倒だと感じたら調べるともっと簡単に表現できる方法が見つかると思います。

日本語でのみご利用いただけます。その他の言語では対応いたしかねます。
Only available in Japanese language. We cannot support it in other languages.