【初心者向け】青空文庫形式からはじめる正規表現



この記事は[青空文庫 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/aozora)の12月4日分のエントリーです。
3日は@takahashimさんの[青空文庫年表（サーバー関連編） 1997-2017で](https://qiita.com/takahashim/items/077a1eeba805c7237cd5)でした。5日は@myokoymさんの[青空文庫をNDC等で絞り込みつつ全文検索できるWebサイト「Aozorasearch」の紹介](https://qiita.com/myokoym/items/a97f6901d4fa12bb8cb9)です。

## はじめに
　青空文庫のファイルは全てプレーンテキストです。それ自体には何の装飾情報もありませんが、高機能なテキストエディタというものは、さまざまな言語に対してシンタックスハイライト（特定の語句や構造を色分けして表示する機能）や入力補完（予測変換のようなもの）を提供して、作業を楽にしてくれます。

　青空文庫の注記に対応しているエディタは[FAQ](http://www.aozora.gr.jp/guide/aozora_bunko_faq.html#midashi580)や、青空文庫をハックするための[リポジトリ](https://github.com/aozorahack/aozorahack)で挙げられています。
　筆者はそのうちの[Mery](https://www.haijin-boys.com/wiki/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9A%E3%83%BC%E3%82%B8)を愛用しています。高機能なのにシンプルで、拡張性に富んだ使いやすさが特長です（ステマ）。適切な設定をすれば以下のように、本文以外の注記をカラフルに色分けして表示してくれます。
　でもそもそも、装飾情報を持たないはずのテキストにどうして色がつけられるんでしょうか？

![1.jpg](https://qiita-image-store.s3.amazonaws.com/0/192482/84f5118a-c1fd-790a-ca25-8e822e8b5617.jpeg)

　ここで使われているのが**正規表現**［regular expression］です。すごくざっくり言ってしまえば、「表記ゆれを含めて検索するための一種の言語」です。これを使ってMeryがテキストに検索をかけ、《ルビ》とか［＃「一」は中見出し］とかいう記述にヒットしたら、テーマが定める色を塗ってあげているんです。

　青空文庫にかぎらず、正規表現は文字情報を扱う上で必須の技術です。マニュアルの[校正編](http://www.aozora.gr.jp/aozora-manual/index-proofreading.html#proofreading04)にも出てくるので、見覚えはあるという方もいらっしゃるのではないでしょうか。プログラミングよりは覚えることは少ないんですが、なかなか奥が深い世界で、Web上の解説を読んだだけだと難解に思えるかもしれません。
　ここではそんな正規表現の初歩を、自分でテキストを色分けしてみるという作業を通して実践的に解説してみたいと思います。

## requirement
- 自力で入力・校正ができる方を対象としています。
- Mery本体のインストールと使い方についてはここでは取り上げません。MeryWikiなどを参照してご自分で行ってください（色分けではなく検索を試みるだけならMery以外のエディタでも可です）。
- 使うテキストは青空文庫形式であれば何でもいいですが、できれば長くていろいろな注記が入っているものが望ましいです。ここでは久生十蘭の長編『[魔都](http://www.aozora.gr.jp/cards/001224/card46076.html)』を使用します。
- 初心者による初心者向けの記事なので、正確さはあまり保証できません。

### 既にMeryを使っている人へ
　Meryは有志による構文ファイルとマクロのおかげで既に青空記法に対応できています。筆者もそれを使っています。が、ここでは正規表現を試すため、いったん導入前の気持ちに戻って新しい編集モードを作ってください。

## 実践
### 最初の見出し（任意の1文字・量指定子）
　さて、とりあえずテキストファイルを取ってきて、Meryで開きます。今この状態では全文黒い字で表示されています。見るからに編集しづらいですね。

　色分けのための仕組みが「編集モード」です。一番下のステータスバーの「Text」をダブルクリックすると、言語の名前がたくさん出てきます。言語が変われば色をつけて強調するべき単語も変わるからです。
　青空文庫の注記に対応したモードはまだありません。「編集モードの設定」から新規作成します。ここでは名前を「MyAozora」としました。

　新しくできたMyAozoraモードのプロパティを開いてみると、まだ真っ白のダイアログが出てきます。Meryにおけるシンタックスハイライトとは、ここに色分けするべき文字列を書き込んでいくという作業になります。

![2.jpg](https://qiita-image-store.s3.amazonaws.com/0/192482/3b8f43ad-de2a-30e2-584f-d170eefb177e.jpeg)

　何をどう書き込みましょう。見出しから手をつけるとして、とりあえず`［＃「第一回」は大見出し］`を追加してみます。

　ちなみに新しい記述を追加した時頭についた1という数字が、属するグループを表しています。クリックすると切り替えられますが、これは“番号”を変えているだけであって“色”を変えているのではないことに注意してください。このダイアログが司るのは「どんな記述が何番か」までで、「何番に何色を塗るか」はテーマ（カラースキーム）の仕事です。
　今はWindows Classicになっていますが、好みによって切り替えてください。

![3.jpg](https://qiita-image-store.s3.amazonaws.com/0/192482/4af03767-dc4b-e8fb-84f8-575cbd3a8fa0.jpeg)

　Windows Classicの1番のカラーである青になりました。ただ、これだと当然第二回以降は黒いままです。『魔都』は十三回まであります。全部列挙していかなければならないんでしょうか？

　もちろんそんなことはありません。「カギカッコの中は何でもいいから、［＃「　」は大見出し］という形の注記をハイライトせよ」と指示してやればいいんです。その指示を、コンピュータが分かりやすいように書いたのが正規表現だとも言えるかもしれません。

　正規表現において、「何でもいい字」は`.`（ピリオド）で表されます。こういった役割を持った字をメタ文字と呼びます。
　`［＃「.」は大見出し］`を追加して、下の「正規表現」というボックスにチェックをつけます。
（選択肢がある時は常に、「正規表現を使う」モードがオンになっているか確認してください）

![4.jpg](https://qiita-image-store.s3.amazonaws.com/0/192482/66d0dea6-18bd-76d3-5ee8-be6071836ddf.jpeg)

　あれ？変わらない。実はこれだと、`［＃「<何でもいい1文字>」は大見出し］`と言っていることになるからです。第十回までなら3文字、それ以降は4文字ですから、それを含めないとなりません。

　繰り返しの回数を表す**量指定子**は、`{m,n}`という書き方をします。「m回以上n回以下」という意味です。`{m}`ならちょうどm回、`{m,}`なら「m回以上なら何回でも」という意味になります。
　この場合は{3,4}ですね。ピリオドにつけてやって`［＃「.{3,4}」は大見出し］`、「内容は何でもいい3文字か4文字の文字列」というパターンができました。

　そしていちいち編集モードのプロパティを開くのが面倒になってきたので、開きっぱなしにできる検索ウィンドウに切り替えます。ここで無事ヒットした検索パターンをテキストファイルに書き溜めていって、後でまとめて追加することにします。

　また「正規表現」にチェックを入れて、検索してみましょう。

![5.jpg](https://qiita-image-store.s3.amazonaws.com/0/192482/bcdcb4c0-cdd8-ac36-1d6a-ebe46f73c6d1.jpeg)

　第十二回まではヒットしました。ところが、今まで言っていなかったんですが、『魔都』は最後の第十三章は「終回」という記述になっています。
　2文字の繰り返しを含めてやれば『魔都』には対応できます。でも他のテキストには対応しきれません。例えば乱歩の『二銭銅貨』には上・下という1文字の見出しが使われていますし、『黒手組』では（上）顕れたる事実・（下）隠れたる事実という見出しで、9文字になります。

　となると正直、数値を設定することに意味がなくなります。`［＃「<内容も数も問わない文字列>」は大見出し］`という指示に切り替えた方が良さそうです。

　そのための特別な量指定子があります。`*`で「0回以上」、`+`で「1回以上」、`?`が「0回または1回」という意味になります。見出しのカギカッコの中が空ということは考えられませんから、ここでは+を使えばいいでしょう。

　`［＃「.+」は大見出し］`で、どんな見出しにも対応できました。

### よりみち
　大見出しはできましたが、青空記法には中見出しと小見出しがあります。しかも形式が先程のような普通のものに限らず、窓見出しなど特殊なものもあります。
　ここで正確を期すために、[注記一覧](http://www.aozora.gr.jp/annotation/)を見てみましょう。

　見出しだけでもけっこうな種類があります。大中小は後で考えるとして、普通の見出し、同行見出し、窓見出しがありそれぞれに、注記をひとつだけ後に置く後置型と注記二つで挟む挟み込み型があるようです。表にしてみるとこんな感じでしょうか。

<table>
	<tbody>
		<tr>
			<td></td>
			<th>後置</th>
			<th>挟み込み</th>
		</tr>
		<tr>
			<th>普通</th>
			<td>○○［＃「○○」は大見出し］</td>
			<td>［＃大見出し］○○［＃大見出し終わり］
<br>
［＃ここから大見出し］<br>
○○○○○○○○○○<br>
○○○○○○○○○○<br>
［＃ここで大見出し終わり］</td>
		</tr>
		<tr>
			<th>同行</th>
			<td>○○［＃「○○」は同行大見出し］</td>
			<td>［＃同行大見出し］○○［＃同行大見出し終わり］</td>
		</tr>
		<tr>
			<th>窓</th>
			<td>○○［＃「○○」は窓大見出し］</td>
			<td>［＃窓大見出し］○○［＃窓大見出し終わり］</td>
		</tr>
	</tbody>
</table>

　これに大中小で全部で18通り（あるいは21通り）ですね。これでまだ見出しだけかよ。
　他にも山ほどある注記の種類をいちいち分類していちいち正規表現を書いていかなきゃならないんでしょうか。

　もちろんそんなことをする必要はなく、簡略化できます。お手本として、MeryWikiで[公開](https://www.haijin-boys.com/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:aozoramsy.zip)されている[急急如律令さん](http://99nyorituryo.hatenablog.com/entry/2013/10/09/220711)作のAozora構文を覗いてみましょう。

![6.jpg](https://qiita-image-store.s3.amazonaws.com/0/192482/e64f072a-439a-9869-1387-271231ff2881.jpeg)

　書かれているのはこれだけです。具体的に定義されているのは見出しだけに見えますね。それも14通りだけです。これでは足りないようにも見受けられます。

　つまりここが、簡略化のしどころです。突き詰めて考えれば青空文庫の注記というのは、`《ルビ》`か、`〔アクセント分解〕`か、`［＃その他の注記］`の3種の形式しかありません。最後の四角い大括弧の中の日本語によって、いろいろなバリエーションの指示を出しているわけです。

　Aozora構文ではその他の注記から見出しにまつわるものだけを独立させ、改ページや訓点といった他の注記はバリエーションを無視して全部一緒くたに扱うことで、構文をシンプルに保っています。
　もちろん意味論的には、全部いちいち分類して正規表現を対応させていったほうがいいんですが、そんなことをしていたら日が暮れるので、テキストエディタの色分け程度だったらシンプルなままでいい、と考えられたのでしょう。

　なので実は見出し周りは最後の関門です。一旦脇に置いておいて、他の注記を色分けしていくことにします。

（個人的には正規表現というのは実際に書くよりも、こんなふうに場合分けして、どこまでを表記の揺れとして1つのパターンに押し込むか考える時のほうが難しいと思っています）

### その他の注記・アクセント分解（貪欲・無欲量指定）
　その他の注記から取り掛かります。`［＃<何でもいい文字列>］`の形ですね。
　これもまた字数制限に意味はないので、大見出しの例が応用できそうです。`［＃.+］`として、検索をかけてみます。

![7.jpg](https://qiita-image-store.s3.amazonaws.com/0/192482/7c593177-1521-d33a-3f45-69ab776d66d5.jpeg)

　だいたいはうまく拾えるんですが、ちょっと変なところが出てきました。現在位置は青緑の部分なんですが、そこからやけに長く水色の部分が伸びて、ここが丸ごとマッチしてしまっていることを示しています。
　これは［＃「へ」に傍点］の始めカッコ＆シャープから、次の外字注記の閉じカッコまでが範囲だと認識されているからです。`［＃`で始まり、間はどんな字でも何文字でもよく、`］`で終わる、という文法通りの動作をしています。

　そうじゃなくて、もっと最初の方にあった閉じカッコで閉じてくれよ、という指示を伝えるためには、後ろに`?`マークをつけます。**非欲張り量指定子**と呼ばれます。上で出てきた「0回または1回」という定義とは**違う意味**になるので気をつけてください。

　正規表現はデフォルトで、最初のような書き方をすると“貪欲な”つまり最長の結果にマッチします。非欲張り指定はそれを解除し、最短の結果を返してくれます。
　実は最初の大見出しの`.+`にもつけておいたほうが安全です。以降もおかしいなと思ったら量指定を見直してみてください。

　`［＃.+?］`で、見出し以外の全ての注記に対応できました。アクセント分解も原理は同じですので、`〔.+?〕`の形で対応できます。

![8.jpg](https://qiita-image-store.s3.amazonaws.com/0/192482/6c656f78-7f20-8dc5-2c24-c9020749566f.jpeg)

### 外字（肯定先読み・後読み）
　外字にだけ注意が必要です。本来入るべき字の代わりに`※`が入っていますが、本文と同化して見落としがちです。これもハイライトしたいです。

　単に`※`で検索するだけでは、外字でない米印までヒットしてしまいミスの元です。注記とセットになった米印ということで、`※［＃.+?］`と書くことはできますし、実際それでも機能します。
　実用的にはそれで問題ないんですが、これだと上でやった注記の定義とかぶっていて、本当は適切ではありません。「米印と注記」にヒットさせるのではなく、「注記の前の米印」にだけヒットさせたいところです。

　正規表現にはまさにそのための機能である、**先読み**と**後読み**があります。今の場合は先読みを使って、こんなふうに書きます。`※(?=［)`

![9.jpg](https://qiita-image-store.s3.amazonaws.com/0/192482/20f152ff-e655-3ac7-5021-7187067e222c.jpeg)

　外字の※にはヒットしていますが、下にある「※底本は」というコメントにはヒットしなくなりました。
　`(?=［)`で、「始めカッコの前の」という意味の指示になります。ちなみに`(?<=］)`とすると「閉じカッコの後の」という意味になり、これが後読みです。
　先読み・後読みには否定形もあり、なかなか難しいパターンです。ここで詳しく解説する余裕はありませんが、興味が湧いた方は応用編として参照リンク先で勉強してみてください。

### ルビ
　ルビは漢字の後ろにつけるカッコだけのものと、前に縦棒がつくものがあります。ボディテキストは黒のままで、ルビ指定のみ色をつけるとすると、単純に`《.+?》`と単独の`｜`を追加すれば良さそうです。

### 再び見出し（選択子）
#### 後置型
　見出しの記述に戻ります。たくさんある見出しのパターンを「揺れ」としてまとめて、一気に扱えるところが正規表現の真骨頂です。

　大・中・小は違うグループ番号を割り振って違う階層にしたいので、ここをごっちゃにするわけにはいきません。最初は大見出しだけから、後置／挟み込みに着目してみましょう。冒頭で追加した記述は消してください。

　後置（前方参照）型の注記には次の3つがあります。

```
［＃「○○」は大見出し］
［＃「○○」は同行大見出し］
［＃「○○」は窓大見出し］
```

　このうち○○の部分は原稿の見出し本文が入るので、また「内容も数も問わない文字列」＝`.+?`ですね。他の部分はだいたい同じで、見出しの前の大／同行大／窓大が違うだけです。
　「大か同行大か窓大」にヒットさせるには、**OR**を表す`|`を使います。こう書きます：`(大|同行大|窓大)見出し`
　丸カッコでくくって半角縦棒で区切ることで、AかBかCのどれかにヒットさせることができます。なお選択肢はいくらでも増やせます。

　前半と合わせて、記述は`［＃「.+?」は(大|同行大|窓大)見出し］`になります。これで後置型はできました。

#### 挟み込み型

　挟み込み型は次の4つです。

```
［＃大見出し］○○［＃大見出し終わり］
［＃同行大見出し］○○［＃同行大見出し終わり］
［＃窓大見出し］○○［＃窓大見出し終わり］
［＃ここから大見出し］
○○○○○○○○○○
○○○○○○○○○○
［＃ここで大見出し終わり］
```

　このうち最後のパターンはちょっと形が違うので外すとして、前の3つは先程と同じような手順で正規表現化できます。
　始めの注記は`［＃(大|同行大|窓大)見出し］`ですね。終わりも同じく、`［＃(大|同行大|窓大)見出し終わり］`になります。

　ここで止めることもできますが、この2つは後ろに「終わり」がつくかつかないかの違いしかないので、もっとまとめることができます。あるかないか＝0回か1回を表す量指定子の`?`を使ってこうします。
`［＃(大|同行大|窓大)見出し(終わり)?］`
　これで重複を少なくできましたが、意味的には始めの注記と終わりの注記は分けておいた方がいいかとも思います。とにかく記述を少なくしたいという方が、まとめたパターンを使うといいでしょう。

　最後に残った複数行のパターンも、原理は同じです。始めと終わりを別にするならそのまま`［＃ここから大見出し］`と`［＃ここで大見出し終わり］`でいいですし、まとめるなら`［＃ここ(から|で)大見出し(終わり)?］`の1行を追加します。

　これで、大見出しについてはどんなものが来ても大丈夫です。中見出しと小見出しについては1文字が変わるだけなので、一括検索＆置換を使いましょう。
　書き溜めた大見出しのパターンを2回コピペして、中見出し用と小見出し用を作ります。中見出し用だけを選択状態にして、置換ウインドウの「検索する文字列」欄は「大」、「置換する文字列」欄は「中」としてすべて置換してください（選択した範囲のみ）。小見出しについても同様です。

　ここで行ったのは単純な置換ですが、正規表現は置換にも威力を発揮します。そのままやっていたのではとても面倒な複雑な書き換えも、一瞬で終わらせたりしてくれる心強いツールです。

### コメント
　ここまで来てしまえば、後は例外としてコメントを定義するだけです。**コメント**というのは、プログラミングにおいて本文に影響を与えない注釈のことです。本体はプログラミング言語で書き、その解説を随所に日本語コメントとして入れておく、というような使い方ができます。

　青空文庫形式においてこれは、[青空文庫収録ファイルへの記載事項](http://www.aozora.gr.jp/guide/kisai.html)で規定されている、冒頭と末尾に入れる情報にあたります。小説本文と分けるために、コメントも定義しておきましょう。
　コメントの定義は編集モードのプロパティの「構文」タブで行います。以下を該当する欄に入れてください。

```text:コメント1
開始：-------------------------------------------------------
終了：-------------------------------------------------------
行コメント：
```
```text:コメント2
開始：底本：
終了：青空文庫作成ファイル：
行コメント：このファイルは
```

　読んで字の如く、冒頭のコメントは線で始まって線で終わり、末尾のコメントは底本表記から始まって「青空文庫作成ファイル：」の行で終わり、補足として「このファイルは～」という宣言が一番最後に入るということを伝えています。
　この情報はどの作品にも必ずこの形で入り、表記ゆれも何もありません。なので直接そのまま定義を書いています。

### 仕上げ
　書き溜めていった正規表現をプロパティに追加していきます。（「正規表現」にチェックを入れるのを忘れずに！）この時後ろで開いているテキストファイルからは選択できないので、他のエディタでファイルを開いて作業するといいでしょう。

　あとはグループを決めてやります。筆者はルビを1番、その他の注記を5番、大・中・小見出しを8・7・6番に振ってみました。ここのグループ分けは好きに行ってOKです。好みのテーマとすり合わせて決めるのもいいでしょう。下はド派手なMonokaiテーマです。

![10.jpg](https://qiita-image-store.s3.amazonaws.com/0/192482/45d768f2-7919-bc52-d89b-c10874213656.jpeg)

　これでもう、立派な青空文庫構文が完成しました。どんな作品を開いても正しく色分けがなされているはずです。実際にここからエクスポートすれば、MeryWikiにも載せられるちゃんとしたmsyファイルが出力されます。
（実際に新版としてアップロードしてみました）

## 終わりに
　駆け足になってしまいましたが、正規表現の基本的な使い方を説明してみました。ゼロから始めたにしては意外と簡単に構文が作れて驚いた方もいるかもしれません。
　ここではエディタに合わせて解説したので、偏った入門編になっています。より本格的には、以下のリンクを辿って勉強してみてください。特に一番最初の連載はおすすめです。

　構文解析はMeryだけではなく、テキストエディタ全般にとって重要な要素です。もしカスタマイズ機能があれば、ここまでの知識を応用して、お使いのエディタを青空対応にできるかもしれません。また小説本文を取り扱うときにも必須の技術です。ここから興味を持って、少しでも多くの人が正規表現を使えるようになれば嬉しく思います。

　是非ともマスターして、一緒に青空文庫の作業をより楽しく、効率よくしていきましょう！


## 参考文献
[初心者歓迎！手と目で覚える正規表現入門](https://qiita.com/jnchito/items/893c887fbf19e17d3ff9) 連載
[手を動かしながら覚える正規表現＜リファレンス＞](http://doc.mas3.net/regexp/reference.html)
[サルにもわかる正規表現](http://www.mnet.ne.jp/~nakama/)