前の日 / 次の日 / 最新 / 2009-08

01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

2009-08-10 Mon

std.regex の captures が残念な動作をする [D言語]

Captures captures();
括弧で指定したサブマッチの情報を、
ランダムアクセスレンジとして取得します。
第一要素はマッチ全体を表します。

Example:
foreach (m; match("abracadabra", "(.)a(.)"))
{
    foreach (c; m.captures)
        write(c, ';');
    writeln();
}
// writes:
// rac;r;c;
// dab;d;b;

rfc3986 の付録 B. 正規表現による URI 参照の解析を使うと、

string uri = "http://www.ics.uci.edu/pub/ietf/uri/#Related";
auto r = r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?";
foreach (m; match(uri, r))
{
    int i = 0;
    foreach (c; m.captures)
    {
        writefln("$%d = %s",i++,c);
    }
}

こんな風に書いたら、

$0 = http://www.ics.uci.edu/pub/ietf/uri/#Related
$1 = http:
$2 = http
$3 = //www.ics.uci.edu
$4 = www.ics.uci.edu
$5 = /pub/ietf/uri/
core.exception.RangeError@std.regex(1667): Range violation

std.regex を見たら、
captures は サブマッチがすべてマッチしているのを前提にしているようだ。
Captures 構造体の front 関数の定義

Range front()
{
    return input[matches[0].startIdx .. matches[0].endIdx];// startIdx endIdx が -1 になる可能性がある。
}

マッチしていないサブマッチも出力するなら、
captures でなく pmatch を使って書く必要がある。

string uri = "http://www.ics.uci.edu/pub/ietf/uri/#Related";
auto r = r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?";
foreach (m; match(uri, r))
{
    foreach (i,c; m.pmatch)
    {
        if(c.startIdx != -1)
        {
            writefln("$%d = %s",i,m.input[c.startIdx..c.endIdx]);
        }else{
            writefln("$%d = <undefined>",i);
        }
    }
}

出力結果

$0 = http://www.ics.uci.edu/pub/ietf/uri/#Related
$1 = http:
$2 = http
$3 = //www.ics.uci.edu
$4 = www.ics.uci.edu
$5 = /pub/ietf/uri/
$6 = <undefined>
$7 = <undefined>
$8 = #Related
$9 = Related

captures はマッチしたものだけを返すなら、
Captures 構造体の front 関数の定義はこうか?

Range front()
{
    if(matches[0].startIdx == -1) popFront;
    return input[matches[0].startIdx .. matches[0].endIdx];
}

Captures 構造体の length 関数の定義はこうか?

size_t length()
{
    size_t count
    foreach (m;matches)
    {
        if(m.startIdx != -1) count++;
    }
    return count;
}