regMatch 正規表現による文字列照合アルゴリズム regNew 文字列照合のための非決定性オートマトンの作成
文字列照合 char *regMatch(char *text, int len, PAIR *nda); 非決定性オートマトンの作成 PAIR *regNew(char *pattern, int patlen);
文字列照合関数において text (入力)テキスト len (入力)テキストの長さ nda (入力)照合パターンに対応した非決定性オートマトン 非決定性オートマトンの作成関数において pattern (入力)照合パターン patlen (入力)照合パターンの長さ
文字列照合関数について 照合パターンがテキストから見つかった場合はそのポインタ、 見つからなかった場合は 0 (NULL)。 非決定性オートマトンの作成関数について 作成した非決定性オートマトン。作成できなかったときは 0 (NULL)。
regNew() を呼び出して非決定性オートマトンを作成した後、 regMatch() を利用して文字列照合を行う。
typedef struct _node { int pass; int reach; struct _edge *edge; } NODE; typedef struct _edge { int c; struct _node *target; struct _edge *next; } EDGE; typedef struct _pair { NODE *start; NODE *end; } PAIR; typedef enum { END, SYMBOL, AND, OR, CLOSURE, LP, RP } TOKEN; #define EPSILON -1
#include "regMatch.h" static int nc; static TOKEN token; static char *pat, *patEnd; static void getToken(void) { if (pat == patEnd) { token = END; return; } switch (*pat) { case '|': token = OR; break; case '*': token = CLOSURE; break; case '(': token = LP; break; case ')': token = RP; break; default: token = SYMBOL; if (*pat == '\\' && pat+1 != patEnd) nc = *++pat; else nc = *pat; } pat++; } static NODE *newNode(void) { NODE *node; if ((node = (NODE *)malloc(sizeof(NODE))) != NULL) { node->pass = 0; node->reach = 0; node->edge = NULL; } return node; } static EDGE *link(NODE *start, NODE *end, int c) { EDGE *edge; if ((edge = (EDGE *)malloc(sizeof(EDGE))) != NULL) { edge->c = c; edge->target = end; edge->next = start->edge; start->edge = edge; } return edge; } static int orNode(PAIR *ret, NODE *start1, NODE *end1, NODE *start2, NODE *end2) { NODE *start, *end; start = newNode(); end = newNode(); if (start == NULL || end == NULL) return -1; link(start, start1, EPSILON); link(start, start2, EPSILON); link(end1, end, EPSILON); link(end2, end, EPSILON); ret->start = start; ret->end = end; return 0; } static int andNode(PAIR *ret, NODE *start1, NODE *end1, NODE *start2, NODE *end2) { link(end1, start2, EPSILON); ret->start = start1; ret->end = end2; return 0; } static int closureNode(PAIR *ret, NODE *start1, NODE *end1) { NODE *start, *end; start = newNode(); end = newNode(); if (start == NULL || end == NULL) return -1; link(start, start1, EPSILON); link(end1, end, EPSILON); link(start, end, EPSILON); link(end1, start1, EPSILON); ret->start = start; ret->end = end; return 0; } static int basicExpr(PAIR *ret) { NODE *start, *end; if (token == SYMBOL || token == RP || token == OR) { start = newNode(); end = newNode(); if (start == NULL || end == NULL) return -1; if (token == SYMBOL) { link(start, end, nc); getToken(); } else link(start, end, EPSILON); ret->start = start; ret->end = end; } else if (token == LP) { getToken(); if (orExpr(ret) != 0) return -1; if (token != RP) return -1; getToken(); } else return -1; return 0; } static int closureExpr(PAIR *ret) { if (basicExpr(ret) != 0) return -1; while (token == CLOSURE) { if (closureNode(ret, ret->start, ret->end) != 0) return -1; getToken(); } return 0; } static int andExpr(PAIR *ret) { PAIR tmp; if (closureExpr(ret) != 0) return -1; while (token == SYMBOL || token == LP) { if (closureExpr(&tmp) != 0) return -1; if (andNode(ret, ret->start, ret->end, tmp.start, tmp.end) != 0) return -1; } return 0; } static int orExpr(PAIR *ret) { PAIR tmp; if (andExpr(ret) != 0) return -1; while (token == OR) { getToken(); if (andExpr(&tmp) != 0) return -1; if (orNode(ret, ret->start, ret->end, tmp.start, tmp.end) != 0) return -1; } return 0; } PAIR *regNew(char *pattern, int patlen) { PAIR *nda; if ((nda = (PAIR *)malloc(sizeof(PAIR))) == NULL) return NULL; patEnd = pattern + patlen; pat = pattern; getToken(); if (orExpr(nda) == 0 && token == END) return nda; return NULL; } static int stateTransit(NODE *node, int c, int reach, int pass) { EDGE *edge; int ret = -1; node->pass = pass; for (edge = node->edge; edge != NULL; edge = edge->next) { if (node->reach == reach && edge->c == c) { if (c == EPSILON && edge->target->reach != reach) { edge->target->reach = reach; stateTransit(edge->target, c, reach, pass); ret = 0; } else if (c != EPSILON && edge->target->reach != reach+1) { edge->target->reach = reach + 1; stateTransit(edge->target, c, reach, pass); ret = 0; } } if (edge->target->pass != pass) { if (stateTransit(edge->target, c, reach, pass) == 0) ret = 0; } } return ret; } char *regMatch(char *text, int len, PAIR *nda) { int k; char *p; static int pass = 2; static int reach = 0; do { k = len; p = text++; nda->start->reach = ++reach; while (1) { stateTransit(nda->start, EPSILON, reach, pass++); if (nda->end->reach == reach) return text - 1; if (k-- == 0) break; if (stateTransit(nda->start, *p++, reach++, pass++) != 0) break; } } while (len-- != 0); return NULL; }
abc|def | abcまたはdef |
a(b|)c | bがあってもなくてもよい。すなわちacまたはabc |
ab*c | bを何回繰り返してもよい。すなわち、ac、abc、abbc、...のいずれか |
このようなパターンの指定のことを正規表現(regular expression)と いう。より形式的に説明すると以下のようになる。ただし、PとQはい ずれも正規表現とする。
正規表現 | 意味(それに一致する文字列) |
文字 | その文字自身 |
\文字 | その文字自身(特別記号 |、(、)、* 等を文字として使う場 合) |
(P) | P |
PQ | PとQをつなげたもの(連結、concatenation) |
P|Q | PまたはQ(選択、union) |
P* | Pの0回以上の繰り返し(閉包、closure) |
正規表現が与えられると、本アルゴリズムではそれをε遷移を含む 非決定性オートマトン(NonDeterministic Automaton)に変換した上で、 その非決定性オートマトンをシミュレートしながらテキストとの照合を 行う。
より高速に照合を行いたいときは、非決定性オートマトンをさらにそれと 等価な決定性オートマトンに変換することが考えられる。