Open Group の正規表現実装

こちらのサイト(http://www.syuhitu.org/other/regex/regex.html)を読んでいたら、気になる文章を発見。

(余談だが、ANSIの規格ではCのライブラリに正規表現ライブラリが含まれることとなっているらしい。 そのため、Borland C++ Compiler 5.5.1では使用できるようになっている。 しかし、なぜか、Visual C++ 6.0では使えないようだ。 最近の奴ではどうなんだろう?)

http://www.syuhitu.org/other/regex/regex.html


えーっ。ANSI C に正規表現!?

ググってみたらそれらしいものを発見。

手元の Ubuntu 12.04 で試したところ、すんなり以下のコードが実行できてしまった。

#include <stdio.h>
#include <stdlib.h>
#include <regex.h>

char str[] = "こんにちは。今日はいい天気ですね。";
void comp(char *re) {
    char *s = str;
    regex_t reg;
    size_t nmatch = 1;
    regmatch_t pmatch;
    int i, off = 0;

    if (regcomp(&reg, re, REG_EXTENDED | REG_NEWLINE) != 0) {
         printf("regex compile failed.\n");
         exit(-1);
    }

    printf("regex: %s\n", re);

    while (regexec(&reg, s + off, nmatch, &pmatch, 0) == 0) {
        int sp = pmatch.rm_so + off;
        int ep = pmatch.rm_eo + off;
        printf("Match position: %d,%d str: ", sp, ep);
        if (pmatch.rm_so >= 0 && pmatch.rm_eo >= 0) {
            for (i = pmatch.rm_so; i < pmatch.rm_eo; i++) {
                putchar(s[i + off]);
            }
            off += pmatch.rm_eo;
        }
        printf("\n");
    }

    regfree(&reg);
}

int main(int argc, char *argv[]) {
    int i;
    char *re[] = {
        "(今日|天気)", // 期待通り
        "。", // 期待通り
        "[^。]+。", // 期待通りではない
        "^.", // 期待通りではない
    };
    printf("String: %s\n", str);
    for (i = 0; i < sizeof(re) / sizeof(re[0]); i++) {
        comp(re[i]);
    }
    return EXIT_SUCCESS;
}


気がついた点など。

  • マルチバイト文字に対応していない。つまり (今日|天気) という正規表現はバイトの連続なのでどちらもマッチするが、[^。]+は [^\xE3\x80\x82]+ と解釈されるため、期待した動作にならない。. も 1 文字ではなく 1 バイトにマッチする。いかにも ANSI な感じだ
  • 最初から最大マッチ数を決めておかなければならないのがよろしくない。こういう設計、他でも見たことがあるけど、何だったかな・・・
  • 機能としてはこのサンプルがほぼすべて(残りはエラー処理)。置換なんかは自力でやらなければならないが、標準 API なのだとしたら、こんなもんなのかな
  • フラグで REG_EXTENDED を指定することで、POSIX 正規表現が使用できるようになるのはかなり評価できる


世の中には正規表現ライブラリは山ほどあり、こんなシングルバイト文字しか扱えないしょっぱいライブラリは選択肢にも挙がらない、どころか認知すらされていないんだろうなぁ、と思った。