正規表現を処理するためのルーチン in C
Copyright (C) 1996,1998 Taiji Yamada <taiji@aihara.co.jp>
[戻る]
正規表現を処理するためのルーチンには Solaris などでは regcmp(3G), regexpr(3G)、GNU
では regex や rx などがあるので自前で用意する必要性もあまりないのですが、国際化の問題や諸々の理由、やはり何事も経験ということで、自分で作ってみました。しかし、一応、国際化を念頭に作成してはいたのですが、まだ国際化した正規表現ルーチンにはなってませんし、後方参照などのいくつか重要な機能が実装されていません。また、効率の悪いところも多々あると思いますが、とりあえずのバージョンとして公開しています。
このプログラムは、状態遷移機械の勉強のために文献 [1] をベースに作り始めたもので、基本的には文献
[1] のサンプルプログラムと仕組みは同じで、これに、任意の一文字「.」や文字クラス「[a-z]」、繰り返し指定「?」「{n,m}」、行頭・行末指定「^,$」、「\t,\n」などの制御文字、「\s,\w」などの文字クラス、「\cG,\xff,\377,\0」などの文字コード指定などを拡張したものです。
Perl と比べて出来ないことは、「\digit」による後方参照や、「\b,\B」のアンカー、GNU
regex と比べて出来ないことは、「\<,\>」のアンカー、POSIX と比べて出来ないことは「[:digit:]」などの文字クラスなどです。これらは、また手が空いた時にでも実装するつもりですが、単語開始位置などのアンカーや後方参照をどのように実装すればベストか今のところ考え中です。
また、正規表現の構文規則のカスタマイズもある程度考慮されているので、AWK,
GREP 互換などにカスタマイズすることは容易で、さらに、デフォルトの構文規則の変更のためのマスク(REGE_SYNTAX_AWK,
REGE_SYNTAX_GREP
など)も用意してあります。しかし、いくつかのクリティカルな挙動で完全な互換にはなっていません。
そして、 国際化対応については、将来的に wchar や wstring への置換えで比較的簡単に出来るように考慮したつもりですが、POSIX
の正規表現の国際化についてきちんと調べたわけではないので、これも詳細は不明です。
いまのところパターンマッチングルーチンしか用意してません。
使い方
基本的に以下のようにすることにより、正規表現によるパターンマッチングが出来ます。
char *from, *to, *p, buf[256], pattern[] = "^$"; /* pattern には正規表現を指定
*/
rege_t *rege;
rege = rege_compile(pattern, 0, NULL); /* 正規表現のコンパイル */
p = buf; /* buf はパターンマッチングを行う対象文字列 */
rege_match(rege, p, strlen(p), &from, &to);
while (from != NULL) {
/* マッチした範囲が次々と *from, *to に代入される */
;
if (p==from && p==to) /* ナル文字列に先頭でマッチした時のため処理
*/
if (*(to+1) == '\0')
break;
else
to++;
p = to;
rege_next_match(rege, p, strlen(p), &from, &to);
}
rege_free(rege);
また、rege_t *rege_compile(char *pattern, rege_syntax_t syntax, char
*trans_table) の syntax に、例えば REGE_SYNTAX_AWK
を指定すれば AWK 互換になり、「\{n,m\}」が繰り返し指定になります。
trans_table にはサイズ 256 の文字コードの変換表を指定することが出来ます。例えば、大文字と小文字を無視するなら、
static char icase_table[256];
int i;
for (i=0; i<256; i++)
icase_table[i] = isupper(i)?tolower(i):i;
rege = rege_compile(pattern, 0, icase_table);
としますが、rege_compile の実行時には trans_table
は使用しないので、 rege_match の直前に rege_t のメンバ
rege->trans_table
にポインタを指定しても同じ効果を持ちます。
rege->trans_table = icase_table;
rege_match(rege, p, strlen(p), &from, &to);
もっとも、大文字と小文字を無視するだけなら syntax に REGE_IGNORE_CASE
をマスクするだけで同じ効果を得ます。
rege = rege_compile(pattern, REGE_SYNTAX_POSIX_EXTENDED|REGE_IGNORE_CASE,
NULL);
その他の詳細については、サンプルプログラム lgrep.c やヘッダファイル、ソースファイルを見て下さい。
また、添付の lgrep.pl は lgrep.c のテストのために作成したもので
Perl による lgrep です。 lgrep は grep と違って
less
のように正規表現にマッチした範囲を表示します。
参考文献
-
近藤 嘉雪, ``C プログラマのためのアルゴリズムとデータ構造 Part 2,'' ソフトバンク,
pp. 41-74, 1993.
このサイトに関するご意見ご要望は taiji@aihara.co.jp
までお願いします。
Copyright (C) 1998 Taiji Yamada, All rights reserved.