vimにはマッチするけどemacsにはマッチしないvimmer的正規表現

vimで検索していたA氏はふと考えた。

> vimとemacsの論争。
> vimはモードの概念がある。
> emacsはOSと呼ばれるほど多機能である。

「**vimは含む**けど**emacsは含まない**行が欲しい！」

つまり、2行目だけを取り出したいとする。
まずは解答。

```vim
/\v^(.*vim)@=(.*emacs)@!.*$
```

## 必要なこと

- vim使いである
- 基本的な正規表現を知っている
    - `^.*[aiueo]$` が何をしているか理解できればOK
- 検索時に`\v`を付け、`verymagic`を有効にする
    - いちいち`(`などをエスケープするのが大変なため

以降、`/`と`\v`は省略。


## 本題

### 前に`vi`がある`m`にマッチさせる

肯定後読みを利用。
`()`でくくり、次に`@<=`を付ける。

```vim
(vi)@<=m
```

これで、手前に`vi`がある場合のみ`m`にマッチする。

たとえば、

vi<font color="Red">m</font>
elvi<font color="Red">m</font>

の`m`にマッチ。

movie
emacs

の`m`にはマッチしない。

よく使うパターンなので、`\zs`という書き方が用意されている（`s`は`start`）。

```vim
vi\zsm
```

### 前に`vi`がない`m`にマッチさせる

否定後読みを利用。
`=`を`!`に変えたら否定。

```vim
(vi)@<!m
```

ca<font color="Red">m</font>
<font color="Red">m</font>iv
e<font color="Red">m</font>acs

の`m`にはマッチするが、

vim

の`m`にはマッチしない。

### 次に`m`が続く`vi`にマッチさせる

肯定先読みを利用。
`<`がなくなり、マッチング対象の後ろに来た形。

```vim
vi(m)@=
```

これは、

<font color="Red">vi</font>m
el<font color="Red">vi</font>m

の`vi`にマッチ。

movie
elvis

の`vi`にはマッチしない。

これもよく使うので、

```vim
vi\zem
```

と`\ze`で修飾できる（`e`は`end`）。

### 次に`m`が続かない`vi`にマッチさせる

肯定先読みの`=`を`!`に変える。

```vim
vi(m)@!
```

<font color="Red">vi</font>
mo<font color="Red">vi</font>e

にはマッチするが、

vim
elvim

の`vi`にはマッチしない。


### `vi`と`m`を含む行にマッチさせる

ここからが本番。
web検索のようにand条件でマッチングさせたい場合。

```vim
^(.*vi)@=(.*m)@=.*$
```

ちょっと複雑だけど、肯定先読みがポイント。

最初の`^`を読み取ったら、`(.*vi)`のチェックが開始。
「なんちゃら`vi`」が含まれているかどうかをチェック。

その後、`(.*m)`のチェックが開始。
「なんちゃら`m`」が含まれているかどうかをチェック。

どちらの条件も満たしていたら`.*$`の部分にマッチ。

この条件では、

<font color="Red">vim</font>
<font color="Red">elvim</font>
<font color="Red">movie</font>

等、行全体にマッチ。

mac
emacs
elvis

にはマッチしない。

`movie`は`m`と`vi`の位置が逆だが、問題なくマッチする。

### `vi`か`m`を含む行にマッチさせる

今度はor検索。
複雑に考えず、基本的な正規表現だけで書く。

```vim
^.*(vi|m).*$
```

文字列中に`vi`か`m`があるかどうかをチェックしているだけ。
`vi`か`m`が含まれていればいいので、

<font color="Red">mac</font>
<font color="Red">movie</font>
<font color="Red">emacs</font>
<font color="Red">vim</font>
<font color="Red">vi</font>
<font color="Red">elvim</font>
<font color="Red">elvis</font>

上記すべてにマッチする。

### `vi`も`m`も含まない行にマッチさせる

「`vi`と`m`を含む行にマッチさせる」を反転させたもの。

```vim
^(.*vi)@!(.*m)@!.*$
```

`=`が`!`に替わっているだけ。

最初の`^`を読み取ったら、`(.*vi)`のチェックが開始。
「なんちゃら`vi`」が**含まれない**ことを確認。

含まれなければ、`(.*m)`のチェックが開始。
「なんちゃら`m`」が**含まれない**ことを確認。

両方の条件を満たしたら、`.*$`にマッチする。

<font color="Red">editor</font>

にはマッチするが、

mac
movie
emacs
vim
vi
elvim
elvis

のどれにもマッチしない。


なお、簡単に書けることは簡単に書く。
`m`が含まれない文字列は`[^m]*`で表現できるので、

```vim
^(.*vi)@![^m]*$
```

と書ける。

### `vi`があるが`m`がない行にマッチさせる

ここまできたら今までの応用で、

```vim
^(.*vi)@=(.*m)@!.*$
```

と、`vi`側は肯定、`m`側は否定にすればいい。

<font color="Red">vi</font>
<font color="Red">elvis</font>

の行全体にマッチするが、

mac
movie
emacs
vim
elvim

にはマッチしない。

また、例のごとく`m`が1文字なのを利用して、

```vim
^(.*vi)@=[^m]*$
```

とも書ける。

## 終わりに

「vimの行だけ見たいんや、emacsは目に入れたくないんや！」というときは、
おもむろに、

```vim
/\v^(.*vim)@=(.*emacs)@!.*$
```

と入力しよう。

> vimとemacsの論争。
> <font color="Red">vimはモードの概念がある。</font>
> emacsはOSと呼ばれるほど多機能である。

`vim`だけが含まれる2行目がハイライトされる。

行全体ではなく`vim`という単語だけが欲しいなら、

```vim
/\v^(.*emacs)@!.{-}\zsvim\ze.*$ " 最初のひとつ
/\v^(.*emacs)@!.*\zsvim\ze.*$   " 最後のひとつ
```

というマッチも可能。



……なお、これはvimじゃなくても肯定先読み、否定先読みができればOK。

たとえばPCREを利用した`-P`オプションが使えるLinuxの`grep`では、

```shell-session
$ grep -P -r '^(?=.*\Walias\W)(?=.*\Wls\W).*$' # \W は単語区切り
```

とやれば、「`ls`に対して`alias`を設定している行だけ」を抽出できる。
まるで検索エンジン！

```shell-session
$ grep -rhv 'emacs' | grep 'vim'
$ sed -n '/vim/p' **/* | sed '/emacs/d'
```

とかはシェル芸に踏み込んでしまうのでここまで。

言い忘れていたけれど、私はemacs好きですよ。