Delphiでマイクロソフト製の正規表現ライブラリを使う

初出:2004/10/04
更新:2008/09/18

<目次>


  1. はじめに
  2. インストール
  3. クラスの内容
  4. コーディング例

<はじめに>

他の開発環境にあるのにDelphiに備わっていない機能の1つに「正規表現」があります。なぜ備わってないのか不思議でなりませんが(Delphi IDEの検索ダイアログには「正規表現」オプションがあるのに…)、ないものは仕方ないのでどこからか調達してくる必要があります。

有名どころでは

などですね。ただ、どれも一長一短で決め手に欠ける感じで、実際に全部試したほうがいいと思います。

で、もう1つの選択肢として、マイクロソフトが提供している「Microsoft VBScript Regular Expression ライブラリ」を使う方法があります。「VBScript」の名の通り、本来はHTMLの中に埋め込んだVBScriptやWindows Scripting Host(WSH)で使うことを想定しています。しかし、本体がActiveX(=COM)なので、ActiveXに対応した開発環境ならば呼び出して使うことが出来るのです。

外部ライブラリの仕様には抵抗を感じる人もいるでしょうが、しかし、

試すだけならタダですから、とりあえず使ってみましょう。

<インストール>

  1. メニューから「プロジェクト→タイプライブラリの取り込み」を選びます。
  2. タイプライブラリの一覧から、「Microsoft VBScript Regular Expression 5.5 (Version 5.5)」を選択します。
  3. 「ユニットの作成」ボタンを押します。

これで「VBScript_RegExp_55_TLB.pas」がDelphiのImportsフォルダに作成されます。

しかし、悲しいことにこのユニットは不完全で、そのままでは使い物になりません。そのため、ソースコードを一部改良します。

◆改良後のソースコードをダウンロードできます。
(このファイルさえあれば、上述したタイプライブラリの取り込み作業は不要です。Importsフォルダに入れるだけで使えるようになります。)

改良前
procedure TRegExp.Set_IgnoreCase(pIgnoreCase: WordBool);
begin
  Exit;
end;

procedure TRegExp.Set_Global(pGlobal: WordBool);
begin
  Exit;
end;

procedure TRegExp.Set_Multiline(pMultiline: WordBool);
begin
  Exit;
end;

procedure TRegExpProperties.Set_IgnoreCase(pIgnoreCase: WordBool);
begin
  Exit;
end;

procedure TRegExpProperties.Set_Global(pGlobal: WordBool);
begin
  Exit;
end;

procedure TRegExpProperties.Set_Multiline(pMultiline: WordBool);
begin
  Exit;
end;
改良後
procedure TRegExp.Set_IgnoreCase(pIgnoreCase: WordBool);
var
  InterfaceVariant: OleVariant;
begin
  InterfaceVariant := DefaultInterface;
  InterfaceVariant.IgnoreCase := pIgnoreCase;
end;

procedure TRegExp.Set_Global(pGlobal: WordBool);
var
  InterfaceVariant: OleVariant;
begin
  InterfaceVariant := DefaultInterface;
  InterfaceVariant.Global := pGlobal;
end;

procedure TRegExp.Set_Multiline(pMultiline: WordBool);
var
  InterfaceVariant: OleVariant;
begin
  InterfaceVariant := DefaultInterface;
  InterfaceVariant.Multiline := pMultiline;
end;

procedure TRegExpProperties.Set_IgnoreCase(pIgnoreCase: WordBool);
var
  InterfaceVariant: OleVariant;
begin
  InterfaceVariant := DefaultInterface;
  InterfaceVariant.IgnoreCase := pIgnoreCase;
end;

procedure TRegExpProperties.Set_Global(pGlobal: WordBool);
var
  InterfaceVariant: OleVariant;
begin
  InterfaceVariant := DefaultInterface;
  InterfaceVariant.Global := pGlobal;
end;

procedure TRegExpProperties.Set_Multiline(pMultiline: WordBool);
var
  InterfaceVariant: OleVariant;
begin
  InterfaceVariant := DefaultInterface;
  InterfaceVariant.Multiline := pMultiline;
end;

<クラスの内容>

この正規表現ライブラリは、4つのクラスを持っています。すなわち

MatchCollection・Match・SubMatchesは、TRegExp.Executeメソッドを実行した場合のみ関係するクラスです。

さらに各クラスを詳細に見ていきましょう。

TRegExp
メンバー型・定義解説
プロパティ
GlobalWordBool正規表現パターンにあたる文字列が検索対象内に複数存在する可能性のある場合、Trueだと複数の検索結果が返ってきます。Falseだと最初に該当した結果のみ返ってきます。
IgnoreCaseWordBool大文字小文字を無視して検索します。
MultilineWordBool検索対象が複数行の場合、Trueだと各行を個別に検索します。Falseだと文字列全体として検索します。これは正規表現パターンの「^」や「$」に影響します。
PatternWideString正規表現パターンを入れます。Perlのようにスラッシュで囲う必要はありません。
メソッド
Executefunction Execute(const sourceString: WideString): IDispatch;
※実際はMatchCollectionが返る
検索対象文字列sourceStringでパターン文字列Patternを実行します。パターンにマッチした情報がMatchCollectionとして返ってきます。GlobalがTrueの場合は複数の検索結果が返ってくる場合があります。sourceStringに検索対象文字列を指定します。
Replacefunction Replace(const sourceString: WideString; replaceVar: OleVariant): WideString;検索対象文字列sourceStringの中でパターン文字列Patternがマッチした場合にその文字列をreplaceVarで置換します。置換した結果が文字列として返ってきます。sourceStringに検索対象文字列を指定します。replaceVarに置換する値を指定します。replaceVarはOleVariant型なので文字列以外に数値なども直接指定できます。
Testfunction Test(const sourceString: WideString): WordBool;検索対象文字列sourceStringの中でパターン文字列Patternがマッチするかテストします。マッチすればTrue、しなければFalseを返します。sourceStringに検索対象文字列を指定します。


MatchCollection
メンバー型・定義解説
プロパティ
CountIntegerItemプロパティの個数です。TRegExp.GlobalがFalseの場合は、0か1となります。
Item[index: Integer]IDispatch ※実際はMatchTRegExp.Executeメソッドの実行結果がMatchとして入っています。TRegExp.GlobalがTrueの場合は、複数の結果が入っていることがあります。
メソッド
なし


Match
メンバー型・定義解説
プロパティ
FirstIndexIntegerパターンにマッチした文字列の検索対象文字列内での位置です。位置は0から始まります。
LengthIntegerパターンにマッチした文字列の長さです。
SubMatchesIDispatch ※実際はSubMatchesパターンに後方参照が含まれている場合、参照結果がSubMatchesとして入っています。
ValueWideStringパターンにマッチした文字列が入っています。
メソッド
なし


SubMatches
メンバー型・定義解説
プロパティ
CountIntegerItemプロパティの個数です。
Item[index: Integer]OleVariant後方参照の結果が入っています。
メソッド
なし

見慣れない型がいくつかありますが、これはActiveX(COM)用の型です。WordBoolはBooleanと同じようにTrue/Falseで使います。WideStringはstringのUnicode版です。OleVariantは何でも入る汎用の型です。詳しくはヘルプを見てください。

<コーディング例>

TRegExp.Testメソッド
uses VBScript_RegExp_55_TLB;

procedure TForm1.Button1Click(Sender: TObject);
var
  Regexp: TRegExp;
  Matched: boolean;
begin
  Regexp := TRegExp.Create(nil);
  try
    Regexp.IgnoreCase := True;      //大文字小文字を区別しない
    Regexp.Pattern := '^delphi';    //正規表現パターン
    Matched := Regexp.Test('Delphi is the product of Borland.');   //テスト
    ShowMessage(BoolToStr(Matched, True));
  finally
    Regexp.Free;
  end;
end;
実行結果:True


TRegExp.Replaceメソッド
uses VBScript_RegExp_55_TLB;

procedure TForm1.Button2Click(Sender: TObject);
var
  Regexp: TRegExp;
  Rep: string;
begin
  Regexp := TRegExp.Create(nil);
  try
    Regexp.Global := True;      //全部を対象とする
    Regexp.Pattern := '明日';   //正規表現パターン
    Rep := Regexp.Replace('明日は明日の風が吹く', 'あした');    //置換
    ShowMessage(Rep);
  finally
    Regexp.Free;
  end;
end;
実行結果:あしたはあしたの風が吹く
<備考>
  • TRegExp.Replaceの返り値はWideString、つまりUnicodeです。そのためLengthで文字数をカウントすると、上記の例では12となります。通常のstringと同じように扱いたい場合は、返り値を入れる変数をstringで宣言します。そこへ代入すれば、自動的にキャストされます。上記の例だとLength(Rep)は24となります(バイトでのカウント)。
  • TRegExp.Replaceの第2引数(replaceVar)はOleVariant型です。そのため、文字列以外に数値もそのまま指定することが出来ます。つまり数値で置換したい場合でもわざわざIntToStrなどを行う必要はありません。


◆後方参照を使った置換
uses VBScript_RegExp_55_TLB;

procedure TForm1.Button3Click(Sender: TObject);
var
  Regexp: TRegExp;
  Source: string;
  Rep: string;
begin
  Regexp := TRegExp.Create(nil);
  try
    Regexp.IgnoreCase := True;
    Regexp.Pattern := '<a\s+href=["'']*([^>"'']*)["'']*>(.*?)</a>';
    Source := '<a href=""http://www.yahoo.co.jp/"">Yahoo! Japan</a>';
    Rep := Regexp.Replace(Source, 'URL=$1, Title=$2');
    ShowMessage(Rep);
  finally
    Regexp.Free;
  end;
end;
実行結果:URL=http://www.yahoo.co.jp/, Title=Yahoo! Japan
<備考>
  • 後方参照を使った置換の例です。正規表現ではカッコ()でくくった部分が後方参照となり、マッチした文字列を抽出することが出来ます。抽出した文字列は、前から順に「$1..$2..$n」として参照できます。


TRegExp.Executeメソッド
uses VBScript_RegExp_55_TLB;

procedure TForm1.Button4Click(Sender: TObject);
const
  Pattern = '<a\s+href=["'']*([^>"'']*)["'']*>(.*?)</a>';
  Source = '<a href="http://www.borland.co.jp/">日本ボーランド</a><a href="http://www.google.co.jp/">Google</a>';
var
  Regex: TRegExp;
  MatCol: MatchCollection;
  Mat: Match;
  SubMat: SubMatches;
  i: integer;
begin
  Regexp := TRegExp.Create(nil);
  try
    Regex.IgnoreCase := True;      //大文字小文字を区別しない
    Regex.Global := True;          //全部を対象とする
    Regex.Pattern := Pattern;      //正規表現パターン
    MatCol := Regex.Execute(Source) as MatchCollection;    //実行
    
    for i := 0 to MatCol.Count - 1 do
    begin
      Mat := MatCol.Item[i] as Match;
      SubMat := Mat.SubMatches as SubMatches;
      Memo1.Lines.Add(Mat.Value);       //マッチした文字列
      Memo1.Lines.Add(SubMat.Item[0]);  //後方参照の文字列
      Memo1.Lines.Add(SubMat.Item[1]);
    end;
  finally
    Regexp.Free;
  end;
end;
実行結果:
<a href="http://www.borland.co.jp/">日本ボーランド</a>
http://www.borland.co.jp/
日本ボーランド
<a href="http://www.google.co.jp/">Google</a>
http://www.google.co.jp/
Google
<備考>
  • 上記の例は、aタグからアドレスとリンク先名を抽出するサンプルです。
    正規表現ではカッコ()でくくった部分が後方参照となり、マッチした文字列を抽出することが出来ます。
  • as演算子を使って型キャストしている個所がいくつかありますが、これは宣言されている型がIDispatchなのでそのままでは使用できず、適切な型にキャストする必要があるためです。
  • GlobalプロパティをTrueにしてExecuteすると、正規表現にマッチする文字列が複数ある場合は、MatchCollectionに複数の結果が返ってきます。上記例では「日本ボーランド」と「google」のリンクが両方ともマッチするので、MatchCollectionには2つのデータが入っています。
  • MatchCollectionに入っているそれぞれの値は、MatchCollection.Item[]で取得でき、これはMatchクラスです。正規表現にマッチした文字列の結果が入っています。
  • MatchクラスのSubMatchesプロパティには、後方参照の結果が入っていて、Match.Item[]で取得できます。


<サンプルアプリケーション>

RegExpを使ったサンプルアプリケーションです。
RegExp_sample010.zip

RegExpサンプルアプリケーション


Copyright © 2004 H'Imagine.
All rights reserved.