第7回正規表現とパターンマッチング

講座「文系のためのawkプログラミング入門」

前回算術関数について学びました。次に文字列関数についても見なければなりませんが、その前に、正規表現について簡単にお話ししておきます。
awkはテキスト処理のツールですから、テキストの構造を記述する厳密な方法をもっています。これを正規表現と呼びます。正規表現について一定の知識をもっていることが、awkでのテキスト処理には不可欠です。awkでは本処理のパターン{アクション}の、パターンを指定するのに正規表現が、よく使われます。従って、ここでは、awkでの正規表現の使用法を、パターンの書き方を例にとって解説しておきます。正規表現自体について、さらに詳しくは、正規表現の講座をご覧下さい。

I メタキャラクタ

文字(列)は、本来、人間が自分自身や自然、社会の事物を記述するために使います。いろんなものの名前や性質などについて書き記すときに使います。ところが、テキスト処理では、この、事物を記述する文字や文字列自体について、厳密に記述する必要があります。そのために、正規表現では、一部の文字について、通常の用法とは異なった使い方をします。正規表現において独自の使い方がなされる文字をメタキャラクタ(超文字)と呼びます。これらは、もともとは普通のテキストのなかで使われていた記号ですが、正規表現の中では、文字(列)について記述する文字として、独自の用法で使われるようになったものです。awkのメタキャラクタを挙げれば、代表的なものは次のようなものです。

awkのメタキャラクタ
.       ピリオドは任意の一文字を表す。

*       直前の文字の0個以上の繰り返しを表す
        【例】  ab*     aの後にbが0個以上続く
                ab.*    ab の後に任意の文字が0個以上続く

+       直前の文字の1個以上の繰り返し
        【例】 ab+     a の後に b が 1個以上続く
                ab.+    ab の後に任意の文字が1個以上続く

^       文字列の先頭をあらわす
        【例】  ^a      先頭に a が来ることをあらわす
                ^ab		先頭に ab が来ることをあらわす

$       文字列の末尾を表す
        【例】  a$ 		文字列が a で終わることを表す
                ab$     文字列が ab で終わることを表す
                ^$		空の文字列を表す。

[ ] 	[ ] の中の任意の文字をあらわす
        【例】  [abc]     a か b か c を表す。
                [ぁ-ん]  全てのひらがな。- で 範囲を指定できる。

|       二つの正規表現の選択。
        【例】  ab|cd     ab か cd を意味する。

()      グループ化する。
        【例】 (ab|cd|ef)   ab か cd か ef を意味する。

\     	次に来る文字、それ自体をあらわす。通常、メタキャラクタの前に
        つけて、そのメタキャラクタとしての意味を打ち消し、通常の文字
        として扱うことを意味する。
        【例】	\.      ピリオドそのものを表す。
                \\	  \ そのものを表す  

正規表現では、まずこれらのメタキャラクタの使い方に慣れる必要があります。

II パターンマッチングと正規表現

awkの本処理は何度も述べてきたように、

パターン{アクション}

という形式をもっています。パターンの部分は、テキストの特性を記述し、入力行がその特性を持っているなら、{アクション}を実行します。テキストがあるパターンを持っているかどうかを試すことをパターンマッチングと言います。ここで、テキストの特性とは、たとえばある文字列を含んでいるか、とか、ある特定の文字で始まる、終わるなどです。こういうテキストのパターンマッチングに、正規表現が使われます。例で示したほうが分かりやすいでしょう。たとえば、入力行の第一フィールドが a という文字で始まるというパターンは、次のようになります。

$1 ~ /^a/{ print}

ここで、半角の〜 は 左辺の文字列が、右辺の正規表現に適合することを意味する演算子です。算術演算子 == は、左辺と右辺が数値として等しいということを意味する演算子ですが、この適合演算子(と仮に呼びます)は、左辺の文字列が、右辺の正規表現に適合するような文字列を含んでいることを意味します。そして反対に含まないことを意味する場合は、~ のまえに ! を置きます。これは算術演算子の不等号( != )と似ています。

$1 !~ /^a/{ print}

そして、上で見られるようにパターンを表す正規表現は/ / で囲みます。実際に使ってみましょう。次のようなデータファイルを、data7.txt として用意します(ダウンロードできます)。

data7.txt
apple egg
hat cat
money sky
absent boy
food note
way arrow
foot cap
value dog
home wall
tree sad

これをおーくの友だちの入力ファイルリストにドラッグ&ドロップして、次のスクリプトで処理します。Input:File Output:Screen に設定します。

# script 7-1
	
$2 ~ /^c/{
	print FNR ":" $2  
}

このスクリプトは、入力行の、第二フィールドが c という文字で始まるというパターンに適合するときだけ実行されます。FNR はファイルの何行目を読んでいるのかを示すawkの組込み変数です。実際に実行して試してください。

2:cat
7:cap

次は入力行がある文字で終わるかどうかのパターンマッチングの例です。

# script 7-2   第1フィールドの単語が e で終わるものを出力

$1 ~ /e$/{
	print FNR ":" $1
}

同様に実行してみてください。結果は以下のようになるでしょう。

1:apple
8:value
9:home
10:tree

次はパターンの否定表現です。

# script 7-3   第1フィールドの単語が e で終わらないものを出力

$1 !~ /e$/{ 
print FNR ":" $1
}

~ の前に ! をおくと否定になります。あるパターンを含まないものを意味します。実行すれば、その結果は以下のようになります。

2:hat
3:money
4:absent
5:food
6:way
7:foot


パターンで、$0 を調べるときには $0 を省略して、正規表現だけを書くことが許されます。次のスクリプトは、$0 すなわち入力行全体が "a" で始まる行を出力します。

# script 7-4

/^a/{                 # $0 ~ /^a/ と同じ意味
	print FNR ":" $0
}

実行して確めてください。

1:apple egg
4:absent boy


このような正規表現によって、文字列のパターンを記述し、実際のテキストがその正規表現に適合するかどうか、即ち、正規表現で表されるパターンを持っているかどうかテストすることをパターン・マッチングの例をもう少しあげましょう。

/^$/{print "---"}               #改行だけの空の行を点線に変える

/^[a-zA-Z]+$/{print}           #アルファベットだけの行を出力
			
/[a-zA-Z]+/{print}             #アルファベットを1文字以上含む行を出力

/^[a-zA-Z \t]+$/{print}        #アルファベットとスペース、タブだけの行を出力

/^[0-9]+$/{print}              #数字だけの行を出力

/^[0-9\.]+$/{print}            #数字と小数点だけの行を出力

/^[aA]/{print}                 # a か A で始まる行を出力

/^(This|That)/{print}          # This か That という語句で始まる行を出力

自分でデータファイルを作って、これらのパターンを試してください。awkはツールですから、よく使って手に馴染ませなければなりません。
パターンの書き方と正規表現の使い方を簡単に見てきました。今回の例はパターンをひとつだけ指定していますが、複数のパターンを指定することもできます。また文字列の比較だけではなく、数式をパターンに含めることもできます。次回はパターンマッチングの応用編です。

前回 次回 目次 Home