"Regular Expression" #5
Riue ちゃんの正規表現講座 / 其伍
さて、今回からは予告通り「egrep レベルの正規表現」について説明していきたいと思います。 なお、今回から「egrep レベルの正規表現」を「拡張正規表現」と呼びます。
最初に、ちょっと寄り道
さて、いざ拡張正規表現の説明をするにしても、「それがどんなツールで使えるのか」ということが分からないと意味がないですよね。 というわけで、最初にちょっとだけ寄り道をして、(やや辞書的な紹介ですが)拡張正規表現が使えるツールやコマンドを紹介しようかと思います。
あくまでも寄り道ですから、興味がない方は拡張正規表現の本論 まで飛ばして下さっても結構ですよ。
egrep
当然と言えば当然ですが、拡張正規表現は egrep でサポートされています。
そう言えば、今まで egrep を含む「grep ファミリ」について一言も説明してなかったですね。 というわけで、ここでちょっと説明しましょう。
「grep」(グレップ)とは、UNIX の文字列サーチ用コマンドです。 grep コマンドに入力されるテキストデータ、または指定したファイルの中のテキストデータから、 指定した文字列を検索するためのツールとして、UNIX な世界では非常によく使われています。 勿論、grep で検索対象として指定する文字列には正規表現が使えます。
簡単な使い方を紹介しましょう。
grep 検索する文字列 ファイル名すると、指定されたファイルから、指定された文字列を含む行だけが表示される…というわけなのです。 ファイル名は複数指定することが出来ますから、 散らかったディレクトリの中で、目的の文字列が含まれたファイルを検索するときなどに重宝します。
また、ファイル名を指定しない場合は、egrep に与えられるテキストデータの中から、該当する文字列を検索します。
grep には 3 種類あり、ed レベルの正規表現をサポートする grep、 拡張正規表現をサポートする egrep(expanded grep)、 正規表現が使用できない fgrep(fixed grep)があります。 fgrep のことを「fast grep」と説明し、「grep ファミリの中では fgrep が一番高速である」 などと紹介している書物が世の中には幾つか転がっていますが、これは誤りです。 grep ファミリの中で(平均的に)一番高速なのは、実は一番強力な正規表現が使用できる egrep です。
もっとも、
- grep
- ed レベルの正規表現が使用出来る(後で述べますが、ed レベルの正規表現でないと使えない機能もあります)。
- fgrep
- 検索対象の文字列は正規表現として解釈されないので、 通常ならメタキャラクタとして扱われる「*」や「.」などが多く含まれた文字列を検索するときに便利。
など、それぞれに使用するメリットっていうものがありますので、egrep だけに存在意義があるわけではないのです。
「grep」という奇妙な名称の由来は諸説ありますが、根本的には同じです。 まず、「Global Regular Expression Print(広域正規表現表示)」から来ているという説。 入力対象となるテキスト、またはファイルの中に書かれたテキストの中から、 対象となる正規表現を検索して表示する、という意味ですね。
また、「g/RE/p」という、ed エディタのコマンドから来ている、という説もあります。
ed の「g」コマンドは「広域(global)」の意味で、
指定した正規表現が存在する行に対して、指定されたコマンドを実行する、という機能を持っています。
「g/RE/p」というコマンドは、
「指定された正規表現(RE)が存在する行を、
『p』コマンドを用いて表示する」という意味になりますが、これはそのまま grep の動作と同じなんですね。
awk
今でこそテキスト処理と言えば perl の独擅場ですが、 perl が出現するまでテキスト処理ツールの王者の地位を守っていたのが、 sed と呼ばれるエディタと、この awk です。
awk は「パターン走査および処理用言語」として位置づけられており、 コマンドというよりもむしろプログラミング言語に近いものです。 「ファイルを先頭から最後まで読んで、その過程で様々処理を行う」 という特性上、幾つかの制約も持ち合わせていますが、C 言語に似た構文と拡張正規表現が使えるということで、 UNIX な世界では長らく重宝されてきました。
「awk」というこれまたけったいな名前は、Aho、Weinberger、 Kernighan という3人の開発者の頭文字を取ったものです。読み方は 通常「オーク」です。
ちなみに、Aho 博士は本当はすごく偉い計算機界の学者なのですが… 日本人にとっては名前がアレなせいか、ちょっとアレですね。
ふう、長い前置きでした。読んで下さった貴方(貴女)、どうもありがとうございました。 それでは、いよいよ本題に入りましょう。
最初から制約の紹介
拡張正規表現は、基本的には grep レベルの正規表現により柔軟な機能を持たせたものです。 しかしながら、実は機能削減も多少行われているんですね。 しかも悪いことに、この削減された機能の中には「何故この機能を省いてしまったんだのか」 と思うようなものも含まれています。
拡張正規表現では、以下のメタキャラクタが使用不可になっています。 「こんなメタキャラクタ、俺は知らんぞ」という貴女は、ぜひこの以前の回を見直して下さい。
\(と\)- 直前にマッチした部分を後から参照するためのメタキャラクタ。
\n- 「n」は1 以上 9 以下の任意の数字。 上に同じく、直前にマッチした部分を後から参照するためのメタキャラクタ。
\<と\>- 単語の先頭と最後を表現するメタキャラクタ。
\{と\}- 繰り返し回数を表現するメタキャラクタ。
というわけで、最初から使えないメタキャラクタが出てきてしまいました。 これらが使えないのは結構痛いのですが、その代わり、 ed レベルの正規表現では表現が面倒で困っていた概念を表現するメタキャラクタが幾つか追加されています(「拡張正規表現」っていうくらいですからね)。
というわけで「+」から
それでは、実際に追加されたメタキャラクタの効能を見ていくことにしましょう。
+
この「+」というメタキャラクタは非常に強力です
。この「+」があるだけでも、拡張正規表現を覚える意味があります。
「+」は、「直前の文字の 1 文字以上の連続」を表現します。
ed レベルの正規表現にもあった「*」
と似ていますが、決定的に違うのは「+」が「1 文字以上」の連続を表現する点です。
「*」は、例えば集合論の観点から見た場合、概念としては非常にに綺麗なものです。
しかし実際に正規表現を使う局面では、「0 文字以上の文字の連続」という概念を表現することはあまりありません。
通常は、「6 が 0 文字以上並んだ文字列」なんていう、一見よくわからないものを検索するよりも、
「6 が 1 つ以上並んだ文字列」や「アルファベットが 1 文字以上並んだ文字列」
といったものを検索するほうが多いわけなのです。
基本的には「*」と同じなので、
そんなに説明はいらないかと思うのですが…ちょっと例を出してみましょうか。
[a-zA-Z]+
先程出した、「アルファベットが 1 文字以上並んだ文字列」を表現する正規表現です。
「*」しか使えない場合ですと、
さしづめ「[a-zA-Z][a-zA-Z]*」とでも表現するところですね。
「+」を使うと、こんなにすっきりと書けるのです。
.+
「任意の文字の 1 文字以上の連続」ということで、要するに文字さえあれば何でも一致してしまう、というものです。
「.*」ですと空文字列にもマッチしてしまいますが、
「.+」なら空文字列はマッチしません。
これは割と定石的に使われる表現だと思いますので、覚えておいて損はないでしょう。
ちなみに「.+」は「*」を使った場合、
「..*」と書くことが出来ます。
To be, or Not to be.
「*」の機能を補完するためのメタキャラクタを、もう一つ挙げてみましょう。
?
「?」は、多少ややこしい性質を持ったメタキャラクタです。
「*」や「+」と同じく、
「直前の文字の連続」を表現するメタキャラクタなのですが…
その意味は「直前の文字の 0 文字以上 1 文字以下の連続」となっています。
こう書いてしまうとちょっと分かりにくいのですが…つまり、 「直前の文字が存在するか、否か」を表現するメタキャラクタだと考えると分かりやすいかと思いますです。
ぜんぜん関係ない話ですが。
「ラスト・アクション・ヒーロー」という映画の中で映画「ハムレット」のシーンがあり、シュワちゃんが馬に乗って銃を乱射してモノを破壊しまくった挙げ句、「To be, or Not to be.」とつぶやくシーンがあったと記憶しているのですが…それがムイミに莫迦で好きでした(「ラスト・アクション・ヒーロー」という映画そのものは、私の中では駄作ですが)。
では、ちょっと例を出してみましょう。
Windows?「Windows」でも「Window」でもマッチする正規表現です。複数形と単数形、どちらも見つけたいような場合に使えますね。
Mac ?OS
「MacOS」でも「Mac OS」でもマッチします。
このように「空白があるかどうか」といった単語も、「?」を使えばエレガントに検索出来るのです。
とりあえずのおしまい
寄り道と無駄話ばかりで、全然講座が進まなかったですね。
今回説明した「+」と「?」は、どちらも
「*」によく似た性質のメタキャラクタですから、
割と簡単に理解されたのでは、と思います。この
3 つはそれぞれ性質が違いますから、使う局面ごとによく考えて、その場その場で最適なものを使うようにして下さい。
次回は、今回説明できなかったメタキャラクタを 2 つ取り上げます。それでは、また。