正規表現を使う文字列の検索・置換は、一部のテキストエディタでは標準機能の一つとなっています。これを自力で実現しようとすると、相当な勉強家でも時間がかかります。しかし、Borland C++ Builder(以後、BCB) や Delphi、あるいは Visual Basic から利用できる DLL があります。BREGEXP.DLL です。
このページは、BREGEXP.DLL の概要とエクスポートされる関数について説明します。また、この DLL を BCB
で使いやすいようにするクラスも紹介します。
BREGEXP.DLL は Perl5 互換の正規表現(日本語対応)をサポートする DLL で、作者は babaq さんです。次のサイトからダウンロードできます。
http://www.hi-ho.ne.jp/babaq/bregexp.html
BREGEXP.DLL 自体は Windows ディレクトリにおいておくといいでしょう。
この DLL の使用・配布については特別な制限はないということなので、かなり広く使われているようです。つまり、それだけ信頼性が高いといえるでしょう。Perl5 互換といっても完全互換ではありませんが、実用上の問題はありません。
DLL は Visual C++ で作られていますので、C/C++ から使いやすいようになっています。Delphi ユーザーの方は 武内 修 さんがこの DLL を Delphi から使えるようにユニットファイルを提供していますので、利用するといいでしょう。このユニットは次のサイトにあります。
このサイトの Delphi - Library のページにある BRegExp ユニットがそうです。
BREGEXP.DLL はフリーウエアですが、フリーウエアソフトにありがちな欠点を持っています。つまり、使用説明書の内容が十分とはいえません。また、作者の好みなのでしょうか、一般的ではない手法で作られていますので、そのままでは使いにくいように思います。そこで、BCB の特性に合わせて、ラッパークラスを自作しました。私のホームページの BCB の Technique のページにあります。
このクラスの使い方を補足するために、サンプルプロジェクトも付いています。プロジェクトは BCB 5.0 で作っていますのが、それ以前のバージョンの人はユニットファイルをコピーして使ってください。
この解説書の後半において、BREGEXP.DLL がエクスポートする関数の解説をしますが、それらの使用例を書きませんでした。しかし、CBRegExp クラスのコードを見れば、関数の使い方は分かると思います。
BREGEXP.DLL は Perl5 をサポートしますので、市販の Perl の解説書を含めて、WEB サイトなどで公開されている資料をあたれば、たいていのことは分かると思います。私のホームページでも、「テキスト加工ツールとしての Perl」と題して、Perl の使い方を解説するドキュメントを公開していますので、ご利用ください。ホームページの Software のページにあります。
なお、「関数の解説」の中では、正規表現についての解説を省略していますが、上記のドキュメントの解説を見てもらえば、理解できると思います。
文字列を検索する
int BMatch( char* regstr, // m 構文による正規表現式 char* target, // 検索対象の文字列 char* targetendp, // 同、NULL 終端文字へのポインタ BREGEXP** rxp, //(戻り値)BREGEXP 構造体 char* msg //(戻り値)エラーメッセージを受け取るバッファ(80 バイト以上を確保する) );
検索に成功したとき 1
それ以外のとき 0
検索パターンと修飾子とを分割するデリミタとして、スラッシュ "/" を使う場合は、文字列のパターンマッチをあらわす m を省略することができる。しかし、スラッシュ自体を操作対象とする場合はスラッシュ以外のデリミタを使わなければならない。その場合は m をつけなければならない。
/検索パターン/修飾子 m@検索パターン@修飾子
| 修飾子 | 意味 |
|---|---|
| k | 日本語として扱う |
| i | 大文字と小文字の区別をしない |
| m | 文字列を複数行として扱う |
| s | 文字列を単一行として扱う |
検索対象の文字列が改行マークを含むとき、デフォルトの s 修飾子の場合は 1 行の文字列として扱う。つまり、必要であれば、改行マーク自体を検索パターンの中に含めることができる。一方、m 修飾子を付けると、改行マークの位置で行を分割し、複数行として認識する。
targetendp は target へのポインタに target の長さ(バイト数)を加えたものである。
msg には BREGEXP.DLL が感知できるエラーに関する情報を返します。TDump.exe を使って調べると、たとえば、正規表現式の構文の間違いや不正なポインタなどを報告するようです。
文字列を置換する
int BSubst( char* regstr, // s 構文による正規表現式 char* target, // 検索対象の文字列 char* targetendp, // 同、NULL 終端文字へのポインタ BREGEXP** rxp, //(戻り値)BREGEXP 構造体 char* msg //(戻り値)エラーメッセージを受け取るバッファ(80 バイト以上を確保する );
正常終了のとき、検索パターンにマッチした数
それ以外のとき 0
s/検索パターン/置換文字列/修飾子
| 修飾子 | 意味 |
|---|---|
| k | 日本語として扱う |
| g | マッチする箇所をすべて置換する(指定しないとき、最初にマッチする箇所のみ置換する) |
| i | 大文字と小文字の区別をしない |
| m | 文字列を複数行として扱う |
| s | 文字列を単一行として扱う(デフォルト) |
BMatch 関数の解説を参照のこと。
この関数の変わった使い方を紹介しておきたい。文字列の置換ではなく、データの並びを入れ替えることもできる。たとえば、
データ:"あいうえお12345"
のようなデータがあるとして、ひらがなの部分と数字の部分の並びを変えてみよう。最初の ( ) で囲んだ部分が $1 に、2 番目の部分が $2 に戻るので、その順番を入れ替えるだけである。char regstr[] = "s/([あ-ん]*)([0-9]*)/$2$1/k"; char target[] = "あいうえお12345"; BREGEXP* exp = NULL; char msg[80]; BSubst(regstr, target, target + strlen(target), &exp, msg); exp->outp に置換後の文字列が戻る。結果は以下のとおり。 "12345あいうえお"
--------------------------------------------------------------------------------
文字列を変換する
int BTrans( char* regstr, // tr 構文による正規表現式 char* target, // 操作対象の文字列 char* targetendp, // 同、NULL 終端文字へのポインタ BREGEXP** rxp, //(戻り値)BREGEXP 構造体 char* msg //(戻り値)エラーメッセージを受け取るバッファ(80 バイト以上を確保する) );
正常終了のとき、変換した文字数
それ以外のとき 0
tr/検索文字の集合/置換文字の集合/修飾子
| 修飾子 | 意味 |
|---|---|
| k | 日本語として扱う |
| c | 検索文字の集合に含まれない文字を変換する |
| d | 検索文字の集合に含まれるが、置換文字の集合に含まれない文字を削除する |
| s | 変換後、連続する同一文字を 1 文字にする |
この構文を使うと、1 バイトの英文字とその位置に対応する 2 バイト文字に変換する。
検索文字の集合と置換文字の集合は 1 バイト文字、2 バイト文字にかかわらず、常に 1 対 1 で対応する。
正規表現式 "tr/aiueo/あいうえお/k" 操作対象の文字列 "abbbcdeefg" 関数の戻り値 3 実行結果(rxp->outp) "あbbbcdええfg"
なお、Perl5 もそうだが、c 修飾子は説明のとおりにはならないようだ。
指定のデリミタで文字列を分割する
int BSplit( char* regstr, // m 構文による正規表現式 char* target, // 操作対象の文字列 char* targetendp, // 同、NULL 終端文字へのポインタ int limit, // 最大分割数 BREGEXP** rxp, //(戻り値)BREGEXP 構造体 char* msg //(戻り値)エラーメッセージを受け取るバッファ(80 バイト以上を確保する) );
検索に成功したとき、分割数
それ以外のとき 0
コンマで区切られた文字列を分割するのであれば、VCL の TStrings オブジェクトの CommaText プロパティで得ることができる。しかし、区切り文字が任意の文字列となると、お手上げである。しかし、この関数を使えば可能となる。たとえば、次に示すような電話番号をデリミタと考える場合である。
操作対象の文字列:"横浜 045-222-1111 大阪 06-5555-6666 東京 03-1111-9999"
検索パターン : " *\\d{2,3}-\\d{3,4}-\\d{4} *"
MSplit 関数の実行結果は、「横浜」、「大阪」、「東京」が得られる。
なお、修飾子には "k" と "i" が使える。
BREGEXP 構造体を開放する
void BRegfree( BREGEXP* rx // 開放する BREGEXP 構造体へのポインタ );
BREGEXP.DLL に含まれる関数は、その実行結果を BREGEXP 構造体に戻す。
BREGEXP 構造体は不要になった時点で、この関数を使って開放しなければならない。
BREGEXP.DLL のバージョン情報を返す
char* BRegexpVersion(void);
BREGEXP.DLL のバージョン情報をあらわす文字列
私が持っている DLL でテストすると、以下の文字列が戻った。
Bregexp.dll V1.1 Build 22 Apr 29 2000 21:13:19
BREGEXP.DLL のバージョン情報をフォームのキャプションに表示する。
Caption = AnsiString(BRegexpVersion());
BREGEXP.DLL の関数の実行結果は BREGEXP 構造体に戻る。また、この構造体は正規表現式のコンパイル結果を保持することで、同じ正規表現式を使う場合の処理の高速化を期待することができる。なお、この構造体は不要になった時点で、BRegfree 関数を使って開放しなければならない。
typedef struct bregexp
{
const char* outp; //(BSubst のときに有効)置換後の文字列へのポインタ
const char* outendp; //(BSubst のときに有効)同、NULL 終端文字へのポインタ
int splitctr; //(BSplit のときに有効)配列の要素数
const char** splitp; //(BSplit のときに有効)配列へのポインタ
int rsv1; // 予約(自由に使用可能)
char* parap; // パターン文字列へのポインタ
char* paraendp; // 同、NULL 終端文字へのポインタ
char* transtblp; //(BTrans のときに有効)変換テーブルへのポインタ
char** startp; // パターンマッチした文字列へのポインタ
char** endp; // 同、NULL 終端文字へのポインタ
int nparens; // パターン中の () の数($1, $2, を調べるときに使用)
} BREGEXP;