2

我正在尝试收集验证 SVG 基本数据类型所需的所有 perl 正则表达式。到目前为止,我有:

my $w        = "\\s*";
my $hexdigit = "[0-9A-Fa-f]";
my $c        = "$w,$w";
my $i        = "[0-9]+";
my $integer  = "[+-]?$i";
my $p        = "${i}%";
my $number   = "(?:$integer|[+-]?[0-9]*\.[0-9]+(?:[Ee]$integer)?)";
my $angle    = "(?:$number$w(?:deg|grad|rad)?)";
my $color    = "(?:#$hexdigit$hexdigit$hexdigit(?:$hexdigit$hexdigit$hexdigit)?|".
               "rgb\\($w$i$c$i$c$i$w\\)|".
               "rgb\\($w$p$c$p$c$p$w\\)|".
               '(?:'.join("|", sort keys %{svgColours()}). '))';
my $length     = "(?:$number(?:em|ex|px|in|cm|mm|pt|pc)?)";
my $coordinate = $length;
my $frequency  = "$number(?:Hz|kHz)";
my $FuncIRI    = "url\(.+\)";
my $numberOptionalNumber = "(?:$number|$number$c$number)"; 
my $paint      = "(?:fill|stroke)";
my $time       = "(?:$number(?:ms|s))";

如果您看到改进的机会,请告诉我。

4

1 回答 1

2

您正在考虑正确的事情(将内容拆分为可组合的语法),但是您这样做的方式存在问题。

最重要的问题是您的许多反斜杠将被忽略。"url\(.+\)" eq "url(.+)","... \. ..."在句点前不加反斜杠。为了避免字符串和正则表达式的不同解析规则,我强烈建议您使用正则表达式引号:qr//. 这具有编译所有这些正则表达式(您实际上并不想要)的副作用,但至少您不必进行双重转义:

my $w = qr/\s*/;
...
my $paint = qr/fill|stroke/; # enclosing group added automatically

但是,这些模式中的每一个都必须作为正则表达式本身有意义。因此,您需要临时变量

my $color_names = join ...
my $color = qr/...|...|$color_names/;

将非正则表达式字符串连接在一起时,您应该习惯于转义所有元字符:

join '|', map quotemeta, keys %{ ... };

代替通过变量插值组成正则表达式,您可以使用(?(DEFINE) ... )

qr/
  (?(DEFINE)
    (?<ws>      \s* )
    (?<comma>   \s*[,]\s* )
    (?<integer> [+-]?[0-9]+ )
    (?<percent> (?&integer)[%] )
    (?<number>  (?&integer)(?: [.][0-9]+ (?: [eE](?&integer) )? )? )
    ...
  )
/x

DEFINE环境中,您可以将模式声明为命名捕获(但它们不捕获,并且您无法在此类模式中捕获)。您可以调用这样的模式,例如(?&pattern).

如果您不想仅仅匹配数据,还想解析它,那么正则表达式可能不适合。我推荐 Marpa::R2 解析器。这有点低级,表达性较差,但有很好的 BNF 语法:

:start   ::= NumberList
:default ::= action => ::array bless => ::lhs
:discard ~ ws

NumberList ::= number+ separator => comma

ws      ~ [\s]+
comma   ~ ','
digits  ~ [0-9]+
sign    ~ [+-]
integer ~ sign digits | digits
number  ~ integer
        | integer '.' digits
        | integer '.' digits [eE] integer
...

阅读Marpa 文档,看看这个库是否有用。否则,Parse::RecDescentRegexp::Grammars是普通正则表达式的不错替代品。如果您选择基于正则表达式的解析器,则可以重用Regexp::Common中的常见模式。

于 2013-09-11T15:38:27.587 回答