Delphi用正規表現ユニット「SkRegExp」

SkRegExpの特長

SkRegExp は Delphi for Win32 用の正規表現ユニットです。Delphi 2005 以降に対応しています。

100% Delphi
SkRegExp は全て Delphi で書かれています。既存の正規表現ライブラリをDelphi に移植したものではなく、ゼロから Delph で書いています。
DLL 不要
外部のライブラリは必要ありません。
Perl 5.10 互換
Prel 5.10 互換の正規表現をサポートしています。
Unicode 対応
SkRegExp は文字列を全て Unicode で処理しています。
Delphi 2007 以前の ANSI 版 Delphi は WideString で、Delphi2009 以降は UnicodeString で処理しています。
  • \w、\d、\sなどの定義済み文字クラスもUnicodeに対応しています。
  • \pでは、General Category、Script、Block に対応しています。
高速
Delphi XE に搭載された TRegEx より最大で2倍高速です。
TRegEx 互換
別配布の SkRegularExpressions.pas を使えば Delphi XE の TRegEx と同じインターフェイスで使うことができます。
TRegEx から SkRegExp 、また、その逆への差し替えがカンタンに行えます。
日本語特有の処理に対応
全角を区別しない、ひらがなとカタカナを区別しないで照合するモードをサポートしています。
ソースコード公開
SkRegExp はソースコードを公開しています。
フリーウェア
SkRegExp は MPL 1.1 のフリーウェアです。ライセンスに従う限り、無料で再配布、利用、改変を行うことができます。

寄付のお願い

SkRegExp の開発をご支援いただける方は、作者への寄付をお願いします。寄付が難しい場合はサイトへの広告掲載と言う形で承りますので、メールアドレス shu アットマーク k.email.ne.jp(アットマークを@に変更してください)までお問い合わせください。

サポートについて

バグ、改善のアイデアなどがありましたらお知らせください。

なお、忙しいときはご返事できないことがあります。サポートにはあまり期待しないで下さい。

2010/1/19、やっつけですがブログを開設しました。SkRegExp についても書きますのでどうぞお越しください。

ダウンロード

最新版

以下のファイルは、添付のヘルプファイル中にある文書と同じものです。この HTML 文書は、あなたが配布するプログラムのドキュメントに、そのまま、あるいは改変して利用することを許可します。

旧版

version 1.0.x は Perl 5.8 相当の正規表現をサポートしてます。

version 1.0.x は旧版です。開発は終了してます。

以下のファイルは、添付のヘルプファイル中にある文書と同じものです。この HTML 文書は、あなたが配布するプログラムのドキュメントに、そのまま、あるいは改変して利用することを許可します。

TRegEx 互換 ライブラリ「SkRegularExpressions.pas」

Delphi XE に追加された正規表現コンポーネント TRegEx を SkRegExp で実現するユニットです。

以下のように、.NET っぽい記述ができます。


procedure TForm1.Button1Click(Sender: TObject);
var
  Group: TGroup;
  Match: TMatch;
  MatchList: TMatchCollection;
begin
  //Memo1.Text 内のすべてのマッチを返す
  MatchList := TRegEx.Matches(Memo1.Text, Edit1.Text);

  //マッチの内容を列挙
  for Match in MatchList do
    for Group in Match.Groups do
      Log(Format('%s [%d, %d]',
        [Group.Value, Group.Index, Group.Length]));
end;

SkRegularExpressions.pas を動かすには、Delphi 2006 以降と SkRegExp version 1.1.10 以降が必要です。

更新履歴

2011/10/15 SkRegExp version 1.3.1 公開
  • 修正)ヘルプに存在しないトピックがあったのを追加。
  • 修正)グループ番号が範囲外だとアクセス違反が発生していたが、例外を発生させるように変更。
  • 修正)グループ名が存在しないとアクセス違反が発生していたが、例外を発生させるように変更。
2011/7/8 SkReExp version 1.3.0 公開
  • 修正)再帰パターンのマッチ結果を TRegEx により近づけた。
  • 新規)先読み/後読みを最適化。
  • 新規)(?!) を最適化。
  • 新規)デバッグ用に、マッチ経過を保持する TSkRegExp.FMatchProcess を追加。
    この変更により、{$DEFINE DEBUG} 時のマッチ速度がさらに遅くなります。
  • 新規)低レベルの文字列比較用に REStrLComp 関数と REStrLIComp 関数を追加。
    ANSI Delphi と Unicode Delphi の違いはここで切り分けることにした。これにより、ANSI Delphi での処理速度が少し速くなる。
  • 変更)すべての文字列比較を REStrLComp と REStrLIComp を使って書き換えた。
  • 変更)未使用のクラス、メソッドを削除。
  • 変更)TREGroup, TREGroupCollection の名前をそれぞれ TGroup, TGroupCollection に変更し、ヘルプで公開した。
  • 変更)TSkRegExp.Groups プロパティをヘルプで公開した。
  • 変更)グローバル関数はクラス関数を呼び出すように変更した。
2011/9/4 SkRegExp version 1.2.7 公開
  • 修正)Delphi XE2 でコンパイルすると警告が出るのを修正。
2011/9/3 SkRegExp version 1.2.6 公開
  • 修正)Delphi XE2 でコンパイルエラー、および、Access violation エラーになる問題を修正。
    原因は、XE2 で PPointerList の定義が変更になったため。
    Delphi XE2 でのみ型 PPointerList を追加することで対処。
2011/7/11 SkRegExp version 1.2.5 公開
  • 修正)文字クラスの範囲指定で、全角半角同一視、ひらがなカタカナ同一視が使えなかったバグを修正。
2011/5/17 SkRegExp version 1.2.4 公開
  • 修正)条件式で、グループ名を条件に指定してもマッチしないバグを修正。
2011/5/14 SkRegExp version 1.2.3 公開
  • 修正)後読みが仕様通りでなかったバグを修正。
    仕様では最上位の選択肢は異なる長さが指定可能だがそうなっていなかった。
2011/5/13 SkRegExp version 1.2.2 公開
  • 修正)先読み、後読み内で繰り返しを使った時、無限ループに陥ることがあるバグを修正。
2011/5/2 SkRegExp version 1.2.1 公開
  • ANSI Delphi でコンパイルできなかったのを修正。ケアレスミス申し訳ありません。
2011/4/29 SkRegExp version 1.2.0 公開
  • 修正)全角半角一致モードで、行頭の ^ と 行末の $ をメタ文字として認識できないバグを修正。
  • 修正)ヘルプの誤植を修正。
  • 変更)TREMatchEngine.MatchPrim 内の繰り返しに関する処理を書き直した。
    冗長、かつ、読みにくかったため。
  • 変更)バックトラック用のキャプチャバッファの保存先を TREGroups.push/pop を使うようにした。
    当初の用途に使わなくなったため。
  • 変更)RegSplit グローバル関数を手続きに変更した。
    関数の意味がないため。
  • 変更)未使用の識別子、メソッドを削除。
  • 変更)TRELexMode の要素を lmOptimize, lmNormal の2つだけにした。
    他の要素を使う予定がなくなったため。
  • 変更)TREStack のフィールドを整理。冗長だったため。
2011/4/27 SkRegularExpression.pas version 0.91 公開
  • TRegEx と互換性のため、関数定義 TMatchEvaluator の AMatch パラメータを const に変更。
2011/4/25 SkRegularExpression.pas version 0.9 公開
  • TMatch.NextMatch が、複数以上マッチがあっても 1 回しかマッチしないバグを修正。
  • このバグは DEKO さんが TRegEx について指摘されているものですが、 SkRegularExpressions.pas も TRegEx を模したものなので同じ問題を抱えています。
    今回の修正は DEKO さんが行った修正をそのまま採用させていただきました。ありがとうございました。
2011/4/22 SkRegExp version 1.1.17 公開
  • 修正)IgnoreWide、IgnoreKana, IgnoreZenHan プロパティが設定できないバグを修正。
  • 修正)再帰パターンの挙動は Perl 5.10 とは異なり、再帰グループ内にはバックトラックしない仕様にした。
    実際には以前からそうだったのだがヘルプに明記していなかった。
  • 変更)TREMatchEngine.MatchRecursion 内の冗長なコードを削除。
  • 変更)TREMatchEngine クラス内の MatchOne メソッドを削除。
  • 修正)ヘルプに DefinedCharClassLegacy プロパティに関する記述がなかったのを追加。
  • 修正)ヘルプの IgnoreZenHan プロパティで構文が property IgnoreWidth: Boolean; となっていたのを修正。
  • 修正)ヘルプの IndexFromGroupName プロパティの戻り値が間違っていたのを修正。
2010/12/26 SkRegularExpressions.pas version 0.8 公開
  • Replace, Split を SkRegExp の Replace, Split を呼び出すように変更。
    この変更により、SkRegularExpressions.pas を使うには SkRegExp version 1.1.10 以降が必須となる。
2010/12/25 SkRegExp version 1.1.16 公開
  • 新規)UnicodeProp.pas の Block を使うかどうかを {$DEFINE BLOCK} で選択可能にした。
  • 変更)未使用のネストレベル付後方参照用のコードを削除。
  • 変更)未使用の文字クラス集合用のコードを削除。
  • 変更)未使用のリソースストリングを削除。
  • 修正)\p で、'Is' ではじまる存在しない Block を指定したとき、エラーメッセージに表示される Block 名が 'In' と表示されるバグを修正。
2010/12/17 SkRegExp version 1.1.15 公開
  • 修正)\X のマッチ範囲が間違っていたのを修正。
  • 変更)\X の繰り返しを最適化した。
  • 変更)冗長なプロパティのアクセスメソッドを削除。
2010/12/12 SkRegExp version 1.1.14 公開
  • 修正)バージョン番号の更新忘れを修正。ケアレスミスを続けて申し訳ありません。
2010/12/11 SkRegExp version 1.1.13 公開
  • 修正)"2010 or later/ja" フォルダ 内の SkRegExpConst.pas 内にリソースストリング sGroupNameIsEmpty が抜けていたのを修正。

今後の予定

ブログをご覧ください。

SkRegExpの中身

SkRegExp は、従来型 NFA エンジンを利用した正規表現ライブラリです。NFA の生成は、ソフトバンクバブリッシングから発行されている近藤嘉雪氏の著書定本 Cプログラマのためのアルゴリズムとデータ構造のアルゴリズムを参考にさせていただきました。アルゴリズムについて書かれた本は難解なものが多いのですが、この本は数少ない入門者向けで、だからと言って手を抜くことなく、しっかりとわかりやすく解説してくれています。

SkRegExp は、構文解析を行いながら構文木と比較オブジェクトを生成し、その後、NFA を生成します。NFA を再帰検索することで正規表現検索を実現しています。

再帰を使うとスタックを食いつぶす危険がありますが、SkRegExp はループを工夫することで深い再帰が起こらないようにしています。SkRegExp で再帰が起こるのは、「繰り返し(?を含む)」、「選択」、「先読み」、「戻り読み」で、これらが何重にもネストされるような正規表現でなければ、スタックオーバーフローは起こりません。詳しくはソースをご覧下さい。ちなみに、この複雑なループが改変を困難にしているらしいです。

プロパティやメソッドは TRegExpr に似せています。このライブラリはロシア生まれですが、結構各国の HP で取り上げられていました。たぶん、Delphi 用では有名なライブラリなのでしょう。SkRegExp も夢は大きく世界進出なので、TRegExpr との互換性を意識しました。

なお、ソースを見ていただければわかると思いますが、SkRegExp には TRegExpr から頂いたものは一行もありません。正直言うと、ソースを見ても何をやっているのかほとんど理解できません。

ところで、たまに質問されるので書いておきます。もし、あなたが自前で正規表現ライブラリを書きたいなら、定本 Cプログラマのためのアルゴリズムとデータ構造に加え、詳説 正規表現を読むことをお勧めします。本書は正規表現のバイブルとも言うべき本です。高い本なので「正規表現を使う」だけなら読む必要はありませんが、「正規表現を作りたい」のであれば古本でも手に入れて読むべきです。

処理速度が遅い件について

以下の情報は version 0.9 当時のものです。現在のバージョンには当てはまりませんが、これも歴史なので残してあります。

ちなみに、SkRegExp verson 1.0 の処理速度は、作者が調べた限り、PCREを使っているTPerlRegExprより速いケースが多いです。もっとも、比較対照のPCRE自体の性能がどうかと言う問題はありますが。


SkRegExp について、処理速度が遅いとの指摘を受けます。

確かに、DFAエンジンを使った bmRegExp には到底敵いませんが、従来型 NFA エンジンを使った中ではましな方だと自分では思っています。

たとえば 手元で調べた限り、TRegExpr とは大きな差はありません。TRegExpr の方が速い正規表現もあるし、SkRegExp の方が速い正規表現もあります。

私が把握している範囲では、SkRegExp が遅いのは次の2つのケースです。

一つは、定義済み文字クラスを使ったときです。SkRegExp の定義済み文字クラスはユニコード対応です。UnicodeProp.pas で巨大なテーブルを検索するため、どうしても TRegExpr に比べると遅くなります。

ちなみに、SkRegExp の場合、DefinedCharClassLegacy を True にすると、定義済み文字クラスが TRegExpr と同じになります。この条件で比較すると定義済み文字クラスを使ったときの差は縮まります。

もう一つは、一致しないときです。ある種の正規表現の場合、TRegExprの方が、一致しないときの終了が圧倒的に速いです。

この理由は、SkRegExp の方が、無駄な正規表現の照合を避ける工夫が不十分だからです。

たとえば、"a+"という正規表現の場合、文字列に"a"が存在しなければ、正規表現エンジンを起動するのはムダです。SkRegExp はこの種の工夫が不十分なため、一致しないときの判断が遅くなっています。

たぶん、処理としては難しいものではないと思います。ぜひ、手を加えてみてください。

その他のソフトウェア

小宮秀一作のその他のソフトウェアは「ソフトライブラリ」ページをご覧下さい。

ブログ

アプリケーション

Delphiライブラリ

PHP