みなさま,あけましておめでとうございます。金宏 和實でございます。新年そうそう「こうしろうのMindStorms日記」をお読みいただきありがとうございます。本年もどうぞよろしくお願いいたします。

 お年玉代わりと言ってはなんですが, 2006年の年初にあたり何かみなさまのお役に立つ話をお送りしたいと思います。SDK 1.4からJavaでも標準で利用可能になった正規表現について数回に渡り解説させていただきたいと存じます。

 インターネットの黎明期にCGIプログラミング言語として一世を風靡した言語といえば,Perlでございます。ラマ本(初めてのPerl第2版 通称「ラマ本」 Randal L.Schwartz,Tom Christiansen著,オライリージャパン)やラクダ本(プログラミングPerl改訂版 通称「ラクダ本」 Larry Wall,Randal L.Schwartz著,オライリージャパン)やらを小脇に抱えるITオタクぽい若者に,筆者のようなおじさんは駆逐されるのではないかとあせったものでした。私もPerlの本は少し読んではみたのですが,すぐにPHPやASPでWebアプリを作成するようになったので,まともなプログラムを書くには至りませんでした。

 Perlの特徴は,なんともいっても文字列の処理が得意,文字列を扱う複雑な処理をマジックのように簡潔に記述できる点です。それを支えるのが正規表現,言葉として発すると「せいきひょうげん」。どんな字を書くのか即座にはイメージできない用語です。うら若き乙女プログラマに説明するときなど,つい言いよどんでしまう言葉でございます。英語ではregular expression,こちらの方も,もうひとつはっきりしない用語でございます。

 Perlの正規表現はプログラム言語界のデファクトスタンダードになっております。Javaの正規表現も.NETの正規表現もPerl互換を謳っております

 さて,正規表現とは何かからお話しを始めましょう。正規表現とは文字列のパターンを表現する表記法で,通常の文字とメタ文字と呼ばれる特別な意味を持った記号を組み合わせて表記します。文字列のチェック,文字列のキャプチャ(文字列の一部を取得する),文字列の置換に利用することができます。

 文字列のチェックの具体的な例をみていただきましょう。RegTest1という名前でクラスを作成してみました。

----------------------------------------------------------

import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class RegTest1 {
 public static void main(String[] args) {
 String ptnStr = "\\d{3}";
 Pattern ptn = Pattern.compile(ptnStr);
 String text ="123"; 
 Matcher mc = ptn.matcher(text);
 System.out.print("1.1:");
 if(mc.matches()) {
  System.out.println(ptnStr+"にマッチします");
 } else{
  System.out.println(ptnStr+"にマッチしません");
 }

----------------------------------------------------------
 Javaで正規表現を使うにはコンパイル済みの正規表現を表すjava.util.regex.Patternクラスと,このクラスの matcher メソッドによって生成され,実際にマッチングを行うjava.util.regex.Matcher クラスをimportします。

 数字のみの入力を受け付け,桁数を制限するというチェックはありがちな仕様でございます。与えられた文字列が,数字3桁にマッチするか否かというチェックを行うための正規表現は\d{3}です。\dは0~9の数値にマッチするメタ文字です。{3}は文字の数を指定します。正規表現を文字列中で使うには,\\ d{3}のように\を\でエスケープする必要があります。

 実行すると,
----------------------------------------------------------

1.1:\d{3}にマッチします

----------------------------------------------------------
と表示されます(1.1は単なる連番ですので気にしないでください)。textに代入する文字列を12や1234に変更するとマッチしませんと表示されます。

 人間というのは,欲深いものでございます。数字3桁であることが簡単にチェックできることがわかりますと,「3桁以上はOK」,「3桁以上,5桁以内はOK」にしたいときはどうすればよいのかと新たなチェック仕様が頭に湧いてまいります。メタ文字の文字数の指定を{n,}とするとn桁以上,{n,m}とするとn個以上,m個以下という意味になります。

 具体的には以下のようなコードになります。
----------------------------------------------------------

 ptnStr = "\\d{3,}";
 ptn = Pattern.compile(ptnStr);
 text ="1234";
 mc = ptn.matcher(text);
 System.out.print("1.2:");
 if(mc.matches()) {
  System.out.println(ptnStr+"にマッチします");
 } else{
  System.out.println(ptnStr+"にマッチしません");
 }
 ptnStr = "\\d{3,5}";
 ptn = Pattern.compile(ptnStr);
 text ="12345";
 mc = ptn.matcher(text);
 System.out.print("1.3:");
 if(mc.matches()) {
  System.out.println(ptnStr+"にマッチします");
 } else{
  System.out.println(ptnStr+"にマッチしません");
 }

----------------------------------------------------------
 上記の2例とも「マッチします」と表示します。

 テキストにある文字列が含まれるかどうか調べることはよくございます。また,言語学などでは出現回数を調べたり,比較することも多いようです。そんなとき面倒なのが,いわゆる「表記のゆれ」でございます。ターボリナックスをTurboLinuxと書く人もいれば,Turbo Linuxと律儀に1スペースをいれる御仁もおられます。「TurboLinux」で検索して,次に「Turbo Linux」で検索,・・・などとやりますと,きりがございません。

 パターン文字列をptnStr = "Turbo\\s*Linux"と指定しますと,TurboとLinuxの間に空白文字(スペースやタブ)が0個以上あってもOKとなります。\s が空白文字を,*が0個以上を表します。0個以上ですから,なくてもよいわけです。
----------------------------------------------------------

 ptnStr = "Turbo\\s*Linux";
 ptn = Pattern.compile(ptnStr);
 text ="TurboLinux";
 mc = ptn.matcher(text);
 System.out.print("2.1:");
 if(mc.matches()) {
  System.out.println(ptnStr+"にマッチします");
 } else{
  System.out.println(ptnStr+"にマッチしません");
 }
 text ="Turbo Linux";
 ptn = Pattern.compile(ptnStr);
 mc = ptn.matcher(text);
 System.out.print("2.2:");
 if(mc.matches()) {
  System.out.println(ptnStr+"にマッチします");
 } else{
  System.out.println(ptnStr+"にマッチしません");
 }

----------------------------------------------------------
 上記のコードの処理結果は,どちらも「マッチします」となるのであります。

 郵便番号やメールアドレスの入力チェックは面倒なものでございます。郵便番号など3桁の数字,続いてハイフン,それから4桁の数字と一通りの入力の仕方に決め打ちできればたいしたことはないのですが,「ハイフンなくても,ええやないか」とか「最初の3桁しかわかんない」というユーザーのわがままに答え出しますと,とたんにややこしくなります。普通に考えればif文の嵐。でも,正規表現を使うと一発でチェックできるのであります。そのへんのお話は次回じっくりとお届けしたいと存じます。