Regular Expression.

●正規表現(2000/8/16)

unixユーザにはお馴染みかとは思いますがそろそろ正規表現の領域に入ってみます。タイトルのregular expressionとはこの正規表現のことです。perlで書かれたcgiプログラムのソースを見たときに以下のような行を見たことはないでしょうか?

$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack( 'C', hex( $1 ) )/eg;

この%([a-fA-F0-9][a-fA-F0-9])という部分が正規表現です。perlではよくパターンという言い方もします。何やら意味不明な記号の羅列に見えるかと思いますがパターンという言い方をするくらいですので、一定の法則に則って記述された記号の羅列によって文字列の規則性(つまりパターン)を表現しようというモノです。

と、一般的な説明をすると訳が判らなくなるのがこの世界の常なので例によってサンプルから先に見ましょう。

$value = '1234abcdef56789';
if( $value =~ /abcdef/ )
{

print "文字列が含まれています\n";

}
else
{

print "文字列が含まれていません\n";

}

printの文字列を見ればifの条件に書かれている内容は想像がつくでしょう。ここでは/で囲まれたabcdefという部分がパターンです。これはつまり変数$valueの文字列中にabcdefというパターンと合致する部分が見つかれば条件が満たされることになります。もしパターンabcdefからeを取り除いてabcdfとすると条件が満たされなくなります。変数$valueの文字列にabcdfという文字列がないためです。

これだけの例ではおそらくどのように便利な機能なのかピンとこないでしょう。それはパターンに通常の文字しか指定していないためです。パターンabcdefは文字通りabcdefを意味し、それ以外の意味を表すことはありません。しかしパターンには特定の意味が定められた文字があり、それらの文字をうまく組み合わせて用いると非常に効果的です。

if( $value =~ /a.c/ )
{

print "aで始まってcで終わる3文字の文字列が含まれています\n";

}
else
{

print "文字列が含まれていません\n";

}

今度はどうでしょうか。ifの条件が満たされるのはprintの文字列にある通りです。パターンa.cacは前述の通りそのままその文字を意味します。焦点は真中のピリオドですね。これが先ほどの特定の意味が定められた文字になります。ピリオドはabといった決まりきった特定の文字を表すのではなく任意の1文字を表します。つまりこのパターンは最初がa、次が任意の1文字、最後がcという3文字の文字列を表しています。真中の文字は任意なのでアルファベットでも数字でも記号でも何でも良いことになります。abcはもちろんaBcazca5ca#c等、これらは何れもパターンa.cと合致します。

これと同じことをもしパターンを使わずにやるとすればaacabcaccadcaec...と全ての組み合わせとひたすら比較していくという作業が必要になります。組み合わせが数える程度しかないのであれば問題ありませんが、多い場合はとても現実的な方法とは言えません。ですがパターンで表現すればいとも簡単です。

ピリオドを説明したので、他も挙げてみましょう。パターンabcではabcは合致しますが、abbcは合致しません。3文字目がcではなくbだからですね。この場合パターンabbcにするという手があります。しかし当然ながらabbbcには合致しません。もしabcabbcabbbcabbbbc...といった文字列全てと合致するようなパターンを書くにはどうすれば良いでしょうか。「最初がa、次は任意の個数のb、次がc」というパターンが必要になります。

任意の個数のbというパターンは繰り返しを意味する*+を使って表現できます。今の場合はab*cというパターンにします。或いはab+cというパターンでも良いでしょう。*+は繰り返しを意味する記号で、共に直前の文字(今の場合はb)の繰り返しという意味になります。b10個でも100個でもちゃんと合致します。

*+はどちらも繰り返しを表しますが1つだけ違う点があります。それは0個を許容するかどうかです。*の場合は0個でも合致しますが+の場合は0個では合致しません。パターンab+cabcabbcabbbcabbbbc...には合致しますがacには合致しません(b0個では合致しません)。一方パターンab*cの場合はabcabbcabbbcabbbbc...に合致し、更にacにも合致します(b0個でも合致します)。言い換えると*は直前の文字の0個以上の繰り返し、+は直前の文字の1個以上の繰り返しということになります。

if( $value =~ /ab+c/ )
{

print "acで挟まれたbがあります\n";

}
else
{

print "見つかりません\n";

}

繰り返し記号としては*+の他に?もあります。しかし?の場合は*+と違い任意の個数の繰り返しではありません。?の場合は個数が0個または1個という条件がつきます。つまりパターンab?cの場合はacabcはそれぞれb0個、1個なので合致しますが、abbcabbbcabbbbc...b2個以上なので全て合致しません。文字の繰り返しというよりも文字の有無を表す記号と考えたほうがしっくりくるかも知れませんね。

if( $value =~ /cas?t/ )
{

print "文中にcatまたはcastがあります\n";

}
else
{

print "ありません\n";

}

他にも特定の文字種を表すモノもあります。\sもそんな文字です。\sホワイトスペースを表す文字です。ホワイトスペースとは普通のスペース(空白文字)の他にもタブ文字と改行文字も含みます。例えばThis isという2つの連続した単語を拾い上げたい場合に使います。この場合はThis\s+isというパターンにすると良いでしょう。\sThisisの間のスペースを表現してくれます。\s+を付けることでThisisがくっついている場合には合致しないようになっています。また単語間が大きく開いていても合致します。\sを指定しているので空白文字以外にもタブで区切られていても合致します。更にはThisisの間で改行されていても合致します。英文テキスト等の場合は行末で改行文字を挟むことになるのでパターンにはスペースよりも\sを使うほうが良い選択と言えます。

if( $value =~ /This\s+is/ )
{

print "文中にThis isがあります\n";

}
else
{

print "ありません\n";

}

\wという文字も比較的使用頻度が高いでしょうか。これはアルファベット、数字、アンダースコアの何れか1文字を表します。これはユーザ名等を表すのに都合がいいように用意されている文字です。\dという文字もあります。これは数字を表す文字です。09のどの数字1文字とも合致し、数字以外には一切合致しません。例えば郵便番号はパターン\d\d\d-\d\d\d\dとすると良いでしょう。これで3桁の数字、ハイフン、4桁の数字から成る郵便番号になります。

if( $value =~ /\d\d\d-\d\d\d\d/ )
{

print "正しい郵便番号です\n";

}
else
{

print "郵便番号ではありません\n";

}

さて\dは数字を表すので小数を表すパターンが書けそうです。おそらく思いつくパターンとしては\d+.\d+といったところでしょうか。整数部、ピリオド、小数部という構成ですが、残念ながらこれではダメです。すぐ判ると思いますがピリオドがマズイですね。小数点のつもりでピリオドを入れていますが、パターンではピリオドには任意の1文字という特別な意味を持たせてあります。このままでは123a456\d+.\d+に合致してしまいます。このような場合には\d+\.\d+とします。ピリオド等のように意味が決まっている文字をそのままその文字として扱うには直前に\を置きます。

if( $value =~ /\d+\.\d+/ )
{

print "小数です\n";

}
else
{

print "小数ではありません\n";

}


戻る