秀丸エディタだけでなく、高度で自由度の高い検索をするには必須の「正規表現」。ここでは実用上重要な例をとりあげながら、わかりやすく解説します。
以下の内容は、秀丸エディタヘルプの「検索系コマンド→検索→正規表現」の内容についての補足です。よってヘルプを参照しながら読まれる事を想定して書かれています。
秀丸エディタは、Ver.3.19までは正規表現エンジンに「JRE32.DLL」が使用されていましたが、 Ver.4.00以降は「HMJRE.DLL」に変更されています。
以下の内容は、正規表現エンジンに「HMJRE.DLL」を使用する前提で書かれています。
まず前章の例の真ん中辺りにあった「.+」ですが、これは言葉で表現した文字の並びのルールの「何か文があって」という部分に対応しています。この記号は「.(ピリオド)」と 「+ (プラス)」との組み合わせで、 それぞれ
. (ピリオド) … 任意の1文字
+ (プラス) … 直前の文字の1回以上の繰り返し
という意味を持っています。
ピリオドは、 MS-DOS のワイルドカード(懐かしい(笑))では「?」と表現していたもので、どんな1文字にもマッチ(*1)します。例えば "あ.た"というパターンは、 "あんた" にも "あいた" にも "あした"にも、 果ては
"あ#た" という 謎のダイイングメッセージ
にもマッチします(笑)。
プラス記号の方はちょっと注意が必要で、これ単独では意味を成しません。必ず直前に文字 があって、その1回以上の繰り返しにマッチします。例えば "痛て+"というパターンは "痛て" という軽い痛みから
"痛てててててて"
という悶絶するような痛み全てにマッチします。 正規表現を使わないと"痛て" と "痛てて" と "痛ててて" と…と全ての可能性をいちいち検索する必要がある所を、 このように
一発!
で指定できるわけです。
今説明したピリオドやプラス記号のような、正規表現の中で特殊な意味を持つ記号のことをメタキャラクタと呼びます。メタキャラクタはこれらの他にも下記の表にあるようにいっぱい(笑)あります。
| 量指定メタキャラクタ | 意 味 |
|---|---|
*
|
直前の文字(※)の0個以上の繰り返しにマッチ |
+
|
直前の文字(※)の1個以上の繰り返しにマッチ |
?
|
直前の文字(※)の0個(=1個もない)または1個にマッチ |
{n}
|
直前の文字(※)の n 個の繰り返しにマッチ |
{n,}
|
直前の文字(※)の n 個以上の繰り返しにマッチ |
{n,m}
|
直前の文字(※)の n からm 個の繰り返しにマッチ |
*?
|
直前の文字(※)の0個以上の繰り返し(最短一致)にマッチ |
+?
|
直前の文字(※)の1個以上の繰り返し(最短一致)にマッチ |
??
|
直前の文字(※)の0個(=1個もない)または1個(最短一致)にマッチ |
{n}?
|
直前の文字(※)の n 個の繰り返し(最短一致)にマッチ |
{n,}?
|
直前の文字(※)の n 個以上の繰り返し(最短一致)にマッチ |
{n,m}?
|
直前の文字(※)の n からm 個の繰り返し(最短一致)にマッチ |
| 位置指定メタキャラクタ | 意 味 |
|---|---|
^
|
(パターンの先頭にある時のみ)行頭にマッチ |
$
|
(パターンの末尾にある時のみ)行末にマッチ |
(?=pattern)
|
後方一致指定(肯定先読み) |
(?!pattern)
|
後方不一致指定(否定先読み) |
(?<=pattern)
|
前方一致指定(肯定戻り読み) |
(?<!pattern)
|
前方不一致指定(否定戻り読み) |
| その他メタキャラクタ | 意 味 |
|---|---|
.
|
(改行文字を除く)任意の1文字にマッチ |
[ ]
|
キャラクタクラス指定(後述) |
( )
|
グルーピング指定(後述) |
|
|
パターンの論理和(後述) |
\
|
エスケープキャラクタ |
(?\tag-number)
|
ヒットした扱いにするタグ指定 |
(?#expression)
|
正規表現パターンに埋め込むコメント。検索では無視される。 |
\Q?\E
|
\Qから\Eの間(\Eがない場合は最後まで)のメタ文字を、通常の文字として扱う。
|
これら以外にも、特定の場所に現れた場合、特殊な意味を持つ記号(キャラクタクラス指定の中の "-" 等)もあります。ちなみに、これら以外の普通に記号と呼ばれている "@" 等は、正規表現では「普通の文字」として扱われます。
"^" と "$" は 【0】の例にも出てきていますが、それぞれ「行頭」及び「行末」にマッチする、となっています。では、以下のような場合について、
"^.+$"という(任意の空でない1行にマッチする)パターンで検索してみたら、どのような結果になるでしょうか?
↓
この文はこの行の右端で折り返されています。 ここのことです→
この行の末尾で [Enter] キーで改行されています。#↓
[EOF]
この場合、見た目の行、 つまり2行目だけ("この文は" から "→"まで)がマッチしそうな所ですが、実際にやってみると2行目の先頭から3行目の行末("#")までがマッチします。この例からもわかるように、正規表現で言う所の「行」とは秀丸エディタの画面上での「見た目の行」のこと ではなく、
ファイルの先頭 または 改行文字 (↓) から、 次の改行文字 または ファイルの末尾 ([EOF]) で区切られた部分
を指しています(いわゆる「論理行」のこと)。上の例では見た目が4行のファイルですが、論理行で数えると
| 見た目の1行目(空ですが(^^;) | = | ファイルの先頭 ? 1行目の改行文字(の手前)まで |
|
見た目の2行目から3行目の末まで |
= | 1行目の改行文字(の次) ? 3行目の改行文字(の手前)まで |
|
見た目の4行目(1行目と同じく空ですが) |
= | 3行目の改行文字(の次) ? ファイルの末尾([EOF])まで |
の3行からなるファイル、と言う事が出来ます。「テキストファイル」は(文字コード体系の違いを除けば)普通の文字(*2)と改行文字の並びで構成されているので、どんな環境でも論理行は同じものになります。元々正規表現は sed等のテキスト整形ツールで使われていたものですが、それらはエディタの様にテキストファイルを「表示」することなくツール内部だけで処理をするため、見た目にはよらない形でテキストを扱うような形式になっていなければならないわけです。
なお、"^" ("$") は、上記のようにパターンの先頭(末尾)にある時だけ上のような特殊な意味を持ちます。つまり "ab^cd$efg"というパターンの中の "^" や "$"
は、単にその文字(ハットやドル)そのもの を表現しています。
ここでちょっと脱線ですが、"$"
と 似て非なる物 として"\n" (改行文字) があります (後述の「エスケープシーケンス」を参照)。直前の話からすると、正規表現では「行末 =
改行文字」なわけで、何でこんな余計なものがあるのか疑問に思う人がいると思いますが、その理由は2つあります。
1つめの理由は、"$" は ファイルの末尾 "[EOF]" も含むということです ("\n" はあくまでも「改行文字そのもの」を表す)。というわけで、最後の行が("↓" ではなく) "[EOF]" で 終わっている場合も"$" を使えば正しく処理できます。
2つめの理由は、置換する時に 改行文字も含めて置換したい場合があるからです。 次の2つのパターン
//.*$ と //.*\n
はどちらも
C++ 言語では // から行末までをコメントとみまします。↓
という行の "//" からそれ以降の文字にマッチしますが、 "$"で行末を指定した場合はマッチの範囲に 改行文字("↓")を含みません。一方 "\n" の方は改行文字もマッチの範囲に含まれます。
このマッチの仕方の違いをうまく利用した置換の例については、 このQ&A集の以下の例を見てください。 - 「●空白行を削除したい」 - 「●空白行を改行だけの行にしたい」
逆に、これら2つを組み合わせて使う場合、意図した結果を得られないことがありますが、その例と解説については第5章をご覧ください。
これは"^"、"$"と同じく位置マッチする正規表現です。幅を持ちません。
指定されたパターンの先頭にマッチします。
正規表現:abc(?=xyz)
検索対象:abcxyz
という場合は、"x"の前にマッチします。("x"の前というか、"c"と"x"の間)
指定されたパターンにマッチしない先頭にマッチします。
正規表現:abc(?!xyz)
検索対象:abcdef
という場合は、"d"の前にマッチします。("d"の前というか、"c"と"d"の間)
指定されたパターンの末尾にマッチします。
正規表現:(?<=abc)xyz
検索対象:abcxyz
という場合は、"c"の後にマッチします。("c"の後というか、"c"と"x"の間)
指定されたパターンにマッチしない先頭にマッチします。
正規表現:(?<!abc)xyz
検索対象:defxyz
という場合は、"f"の後にマッチします。("f"の後というか、"f"と"x"の間)
後方一致指定(肯定先読み)と後方不一致指定(否定先読み)は使いかたによっては、条件の絞り込みにも使えます。
hidemaru000
hidemaru001
hidemaru002
hidemaru003
hidemaru004
hidemaru005
hidemaru0A0
hidemaru0A1
hidemaru0A2
hidemaru0A3
hidemaru0A4
hidemaru0A5
というデータから、
後方一致指定 :hidemaru(?=\d{3}).+[25]$
で検索すると、"hidemaru"の次が3桁の数字で、末尾が 2 or 5 にマッチする、
hidemaru002
hidemaru005
に、マッチします。
後方不一致指定:hidemaru(?!\d{3}).+[25]$
で検索すると、"hidemaru"の次が3桁の数字でない、末尾が 2 or 5 にマッチする、
hidemaru0A2
hidemaru0A5
に、マッチします。
前節で説明したメタキャラクタのうち、"." (任意の1文字にマッチ)は非常に使える表現で、 これがなければ正規表現は
全く使い物にならないと言っても過言ではない
でしょう。 ですが、逆に
強力すぎて使えない
場面にもしばしば出会います。 例として以下のクイズを考えてみましょう。
ここで、「英単語」とは 「半角の
a ? z, A ? Zのみからなる文字の並び」なわけですから、もし「任意の半角アルファベット1文字にマッチするメタキャラクタ」があれば、 前節で説明したメタキャラクタ "+" と
合わせて一発!!
です。が、幸か不幸か正規表現にはそんなメタキャラクタは用意されていません。 ※ : "\w" はアンダースコア "_" を含むため、今の目的には使えません。
が、落胆するのはまだ早いです。 何と正規表現には任意のアルファベット どころか、「自分で勝手に定義した任意の文字の集まりにマッチするメタキャラクタ」を作るルールが用意されているのです(わーいわーい)!それがいわゆるキャラクタクラス指定 です。
例えば "a", "b", "c", "d", "×" の5文字のうちのどれか1文字にマッチするメタキャラクタが欲しい時は、
[abcd×]
という風に、 含めたい文字を "[" と
"]" で囲むだけでよいのです。メタキャラクタ "." が(改行文字を除く)任意の1文字にマッチするのに対し、キャラクタクラス指定はそれより狭い範囲の文字の集まりの中の1文字にマッチするわけです。
では、先のクイズに戻って「任意の半角アルファベット」にマッチするキャラクタクラスを作りましょう。もちろん
[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]
で正解なのですが、これは以下のようにもっと短縮した形で書く事ができます。
[A-Za-z]
ここで、"-" は (文字コードで見て)その前後の間にある文字を全て含める、という意味になっています (というわけで "[Z-Az-a]"
は 間違い)。
さて、キャラクタクラス指定によって「任意のアルファベットを表す」表現が出来たので、あとはそれを1個以上繰り返す、という指定をすればよいわけです。というわけで結局クイズの答は
[A-Za-z]+
ということになります。
ここで、いくつかの典型的なキャラクタクラス指定を紹介します。
| キャラクタクラス | 意味 |
|---|---|
[0-9]
|
半角の数字にマッチ |
[ぁ-ん]
|
全角ひらがなにマッチ |
[ァ-ヶ]
|
全角カタカナにマッチ |
[ -~]
|
任意の1バイト文字(半角空白" "から"~"まで)にマッチ |
[亜-黑]
|
漢字にマッチ (詳細は●漢字だけを探したいを参照) |
キャラクタクラス指定は 「任意の」という範囲が広すぎる "."
に比べて、より狭い範囲の文字の集まりに限定してマッチさせるものでしたが、この逆、つまり「指定したいくつかの文字以外の1文字にマッチする」ような指定はできないのでしょうか?
…と白々しく(笑)ネタを振るぐらいなのでちゃんとそういうことができるようになっていて、 "[" と "]"の代わりに "[^" と "]" で囲むと、「その間にある文字以外の1文字(改行文字も含む))」という意味の指定になります (「[^0-9](半角数字でない1文字)」etc.)。
ここでよく勘違いされるので注意が必要ですが、例えば "[^abc]"という指定は 「"abc" でない文字列」という意味 ではなく、「"a"でも "b" でも "c" でもない1文字」という意味になります。
A[^BCD]E" と指定する etc.
…ここまでくると、 「じゃあ "^" や "-"をキャラクタクラスに入れたい時はどうするの?」という声が聞こえてきそうですが、これもしっかり逃げ道 があって、"^"
は "[" の直後以外、 "-" は "[" (または"[^") の直後か "]" の直前に書けば、キャラクタクラス指定に入れることができます。
あ、そうそう。メタキャラクタは キャラクタクラス指定の中では(エスケープキャラクタを前に置かなくても自動的に)
その特殊な意味を失って、単にその文字自身を表わすようになります。ただし "]" は例外で、ちゃんと "\" を前に付けないと駄目です…って当たり前ですが(^^;。
また、よく使用されると思われるキャラクタクラスの省略記法があります。
| \s | 空白([ \t\r\n]と同じ) |
| \S | \s以外 |
| \d | 数字([0-9]と同じ) |
| \D | \d以外 数字の否定クラス[^0-9] |
| \c | 英数字と「_」([a-zA-Z0-9_]と同じ) |
| \C | \c以外 |
| \i | 英字と「_」([a-zA-Z_]と同じ) |
| \I | \i以外 |
| \y | 数字を含む単語の区切り(\<|\>と似ているけど数字も判断) |
| \Y | \y以外の区切り |