検索
連載

使うほどに良さが分かる正規表現Windowsユーザーに教えるLinuxの常識(6)(2/2 ページ)

最初は訳が分からないが、慣れると大変重宝する。ある意味UNIXの象徴ともいえるのが「正規表現」だ。正規表現をすべて究める必要はないが、知っていると便利なことが多い。

Share
Tweet
LINE
Hatena
前のページへ |       

初歩的なパターン指定

 では簡単そうなところから順番に紹介していきましょう。

任意の1文字「.」

 まずは任意の1文字と一致する「.」です。数字やアルファベット、記号など、文字種は問いません。DOS/Windowsのワイルドカードの「?」に相当すると考えればいいでしょう。

例:「1.3」は「1で始まって3で終わる3文字の文字列」を表す。113、193、1a3など

 ちなみに、「.」自体に一致してほしいときはどうすればよいのでしょうか? その場合は「\.」と書きます。これに限らず、\を直前に置かれた文字は特別な意味を失い、そのままの文字を意味することになります。正規表現でファイル名を指定するときに「main.c」と書くと、目的の「main.c」にもマッチしますが、「main2c」などといったファイル名にもマッチしてしまいます。正確を期すためには「main\.c」と書かなければなりません。

0回以上の繰り返し「*」

 次はワイルドカードの「*」に相当するパターンを紹介しましょう。実は、正規表現でも同じ文字を使います。ただし、その意味が微妙に違います。ワイルドカードでは「任意の複数文字に一致」ですが、正規表現では「直前の文字を0回以上繰り返したものに一致」です。ですから、Windowsでファイルを指定するときなどの「*」と同じ意味にするには「..*」と書かなければなりません。これを「.*」としてしまうと、「任意の1文字を0回以上繰り返したものに一致」になって、結局文字がなくても一致してしまいます。

例:「ab*c」が一致するのは、ac、abc、abbc、abbbcなど

直前の文字の0〜1回の繰り返し「?」

 「?」も似たようなものです。Windowsのワイルドカードとは違って、「直前の文字を0回または1回繰り返したものに一致」します。

例:「AB?C」に一致するのは、ACかABC

 なぜこのような仕様になっているのでしょうか。直接確かめたわけではありませんが、Windows方式では0文字、いわゆるヌル文字を表現できません。しかし正規表現なら手間は増えますが1回以上の繰り返しも表現できます。より汎用性を高めるために、このような仕様になっているのではないでしょうか。

直前の文字の1回以上の繰り返し「+」

 ただ、さすがにこれでは使いにくいと思った人がいたのか、最近では「直前の1文字を1回以上繰り返したものに一致」する「+」が使えるプログラムが増えています。

例:「ab+c」に一致するのは、abc、abbc、abbbcなど。acには一致しない

行頭「^」と行末「$」

 文字ではなく、行の先頭に一致するのが「^」、終わりに一致するのが「$」です。冒頭で述べたように、行の最初や最後にある余分な空白を削除するときなどに便利です。「 *」(「*」の前に半角スペースが2個あることに注意)だと、1文字以上の空白すべてに一致してしまいます。日本語は分かち書きをしないので文中に空白が入ることは少ないのですが、英単語を並べたり、プログラムを作ったりしているときはいくらでも空白が入ります。そこで行頭の空白を指定するには「^ *」(「^」と「*」の間に半角スペース2個)、行末の空白を指定するには「 *$」(「*」の前に半角スペース2個)としてやります。

 また、Windowsで作成したテキストファイルをLinuxで扱うと、行末のCRが余分になりviなどでは「^M」と表示されます。nkfなどの文字コード変換ツールにも行末のCRを削除したり、CRを付け加えたりするオプションを持つものがあります。しかし、これも「^M$」として指定すればテキストエディタで削除できます。例えば、

:%s/^M$//

です。ちなみに、「^M」は2文字ではなくこれだけでCRを表す1文字です。viなら[Ctrl]+[v]に続けて[Ctrl]+[m]、Emacsなら[Ctrl]+[q]に続けて[Ctrl]+[m]で入力できます。同じような置換はsedなどでもできますから、かつては簡単なシェルスクリプトとして作っておいたものです。

どれかに一致「[]」

 任意の1文字ではなく、特定の文字に一致してほしいときもあります。特定の文字列ならそのままつづればOKですが、例えば「母音だけが並んだ文字列」を指定したいときはどうすればよいのでしょうか。この場合は「[」と「]」を使います。この2つで挟むと、「中に並べられた文字のどれかに一致」という意味になります。先ほどの「母音だけが並んだ文字列」なら、

[aiueo][aiueo]*

です。同じものを2つ並べているのは、前述のように「*」が「0回以上の繰り返し」を意味するからです。ここでも「+」が使えれば、

[aiueo]+

で済みます。

 もし、並べる文字に規則性があるなら、省略形も使えます。始まりの文字と終わりの文字をハイフンでつなげればよいのです。「[a-z]」とすれば英小文字のいずれか、「[0-9]」とすれば数字のいずれかに一致します。日本語化されたプログラムなら漢字でも同じことができますが、正確にいうとこれは文字コードの順で並んでいるときにしか使えません。「[一-九]」くらいならまだしも、普通の漢字ではぱっと見には分かりませんね。

 ではちょっと応用してみましょう。まず、

[a-zA-Z][a-zA-Z]*

で英単語に一致します。

[a-zA-Z0-9_\-][a-zA-Z0-9_\-]*

とすれば変数名や関数名に一致、といったところでしょうか。場合によってこれを「\w」で表せることもあります。

いずれかの文字以外(否定)「[^」

 「いずれかの文字」ではなく、「いずれかの文字以外」という指定もできます。この場合は「[」の直後に「^」を置きます。

[^aiueo]

で、「母音以外の1文字」ということになるわけです。

複数パターンのいずれか「|」

 文字ではなく複数のパターンのいずれか、というときは「|」を使います。

abc|def

とすれば、「abc」か「def」のどちらかに一致します。ただし、処理系によって「|」だったり「\|」だったりするので注意が必要です。

ちょっとした応用編

パターンの繰り返し

 時として、「一致する回数を指定する」という表現をしたくなります。プログラム言語によっては変数名の長さに制限があり、例えば制限が8文字だったとしましょう。9文字以上の変数名でエラーを起こしてくれればましなのですが、間抜けな処理系だとエラーを起こさず実行できるくせに、先頭8文字しか判別に使わない、などということがあります。最近は分かりやすいように長い変数名を使うのが一般的ですから、先頭8文字が一致した変数もあり得ます。こうしたとき、長さ9文字以上の変数名らしき文字列をピックアップしてチェックしたくなるでしょう。

 真っ正直に書けば、

[a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-]
[a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-]+

といったところでしょうか。このくらいならまだ何とかなります。しかし100文字といわれたらうんざりしますし、絶対どこかでタイプミスします。第一、本当に100個あるのかどうか確認するだけでも大変です。

 もちろん、これをすっきり書く方法も用意されています。9文字以上なら

[a-zA-z0-9_-]{9,}

です。「{n}」で直前のパターンをn回繰り返し、「{n,}」だとn回以上、「{n,m}」ではn回以上m回以下を意味します。実際には、

$ egrep "[a-zA-Z][a-zA-Z0-9_]{9,}" *.c

とでもするのが第1段階です(Cでは変数名や関数名の先頭は英数字。これに英数字かアンダースコアが続く)。これで怪しいところに見当をつけられますから、そこに関連する部分に絞り込んで調査するわけです。

グループにまとめる

 正規表現をグループ化するには、「(」と「)」でくくります。

(abc)+(def)+

で「abcを1回以上繰り返した後にdefを1回以上繰り返したもの」になるわけですが、処理系によっては使えないこともあるので確認が必要です。また、「\(」と「\)」だったりもします。こちらの表記では、後方参照時に使うためのグループ化であることもあります。

 後方参照とは、実際に一致した文字列を表すためのものです。具体例を挙げると、「dat」に対して「\([abc]\)」でパターンマッチをかけると、「\1」と書くことで「a」が取り出せます。ちょっと分かりにくいと思いますが、応用例として音引きを追加するときに便利です。

 実際に「プリンタ」を「プリンター」にする場合を考えてみましょう。単純に、

s/プリンタ/プリンター/g

とすると、もともと「プリンター」があった場合は「プリンターー」に置き換わってしまいます。では、音引き以外が続いていたら音引きに置き換える

s/プリンタ[^ー]/プリンター/g

ではどうでしょう。今度は「プリンタを」が「プリンター」となって助詞が消えたりします。

 Windowsの世界だと、これはエディタで置換を行い、置換対象が見つかるたびにいちいちユーザーが置き換えを行うかどうか指定するのが一般的です(編注)。分かりやすいといえば分かりやすいのですが、100カ所も200カ所もあると時間がかかりますし、その間ユーザーが張り付いていなければなりません。また、何より途中で絶対に間違えます。

編注:Windowsの単純な置換コマンドで行う場合は、一度「プリンター」を「プリンタ」に全文置換し、あらためて「プリンタ」を「プリンター」に置換することで、「プリンターー」になるのを防ぐことができる。いずれにせよ、正規表現の方がスマートではある。

 こんな場合、正規表現では

s/プリンタ\([^ー]\)/プリンター\1/g

と指定します。これで、

「プリンタ」に音引き以外の文字が続いていたら「プリンター」に置き換えて、もともと続いている音引き以外の文字を加える

ことになるわけです。結果として、「プリンタ」は「プリンター」に、「プリンター」はそのままという望みの置換が行われます。

 ちなみに、音引きを取るのは簡単です。

s/プリンター/プリンタ/g

で済んでしまいます。

 もう1つ例を挙げてみましょう。「行なう」の活用形すべてに対して、送りがなの「な」を削除したいとします。vi(バージョン、というかバリエーションにもよりますが)ならば、

s/行な\([わいうえ]\)/行\1/g

と書くことで、一度の置換で作業が終了します。この場合は単純に、

s/行な/行/g

でも目的は達成できそうですが……。

 ただし、この機能はどのプログラムでも使えるわけではありません。PerlやRuby、sedあたりがこれを備えたプログラムの代表です。このように、文字列処理に強いといわれるツールは強力な正規表現機能を備えています。


Copyright © ITmedia, Inc. All Rights Reserved.

前のページへ |       
ページトップに戻る