我该如何用字符串创建类似否定字符类的东西?

4
我正在尝试为Perl编写Mustache的分词器。我可以轻松处理大多数这样的标记:
#!/usr/bin/perl

use strict;
use warnings;

my $comment  = qr/ \G \{\{ !  (?<comment>  .+? ) }}              /xs; 
my $variable = qr/ \G \{\{    (?<variable> .+? ) }}              /xs; 
my $text     = qr/ \G         (?<text>     .+? ) (?= \{\{ | \z ) /xs; 
my $tokens   = qr/ $comment | $variable | $text /x;

my $s = do { local $/; <DATA> };

while ($s =~ /$tokens/g) {
    my ($type)    = keys %+;
    (my $contents = $+{$type}) =~ s/\n/\\n/;

    print "type [$type] contents [$contents]\n";
}

__DATA__
{{!this is a comment}}
Hi {{name}}, I like {{thing}}.

但是我在Set Delimiters指令方面遇到了麻烦:
#!/usr/bin/perl

use strict;
use warnings;

my $delimiters = qr/ \G \{\{    (?<start> .+? ) = [ ] = (?<end> .+?) }} /xs; 
my $comment    = qr/ \G \{\{ !  (?<comment>  .+? ) }}                   /xs; 
my $variable   = qr/ \G \{\{    (?<variable> .+? ) }}                   /xs; 
my $text       = qr/ \G         (?<text>     .+? ) (?= \{\{ | \z )      /xs; 
my $tokens     = qr/ $comment | $delimiters | $variable | $text /x;

my $s = do { local $/; <DATA> };

while ($s =~ /$tokens/g) {
    for my $type (keys %+) {
        (my $contents = $+{$type}) =~ s/\n/\\n/;

        print "type [$type] contents [$contents]\n";
    }
}

__DATA__
{{!this is a comment}}
Hi {{name}}, I like {{thing}}.
{{(= =)}}

如果我把它改成

my $delimiters = qr/ \G \{\{ (?<start> [^{]+? ) = [ ] = (?<end> .+?) }} /xs;

它运行良好,但Set Delimiters指令的目的是更改定界符,因此代码最终会看起来像这样。
my $variable = qr/ \G $start (?<variable> .+? ) $end /xs;

而且说{{{== ==}}}是完全有效的(即将定界符更改为{==})。我想要但可能不需要的是能够说类似于(?:not starting string)+?这样的内容。我觉得我只需要放弃对其进行清洁处理并将代码放入正则表达式中以强制它仅匹配我想要的内容。我试图避免这种情况,有四个原因:
  1. 我认为这样做不太干净。
  2. 它被标记为实验性的。
  3. 我不太熟悉它(我认为它归结为(?{CODE})和返回特殊值)。
  4. 我希望有人知道我不熟悉的其他奇特功能,更适合这种情况(例如(?(condition)yes-pattern|no-pattern))。

只为了让事情清楚(我希望如此),我正在尝试匹配一个恒定长度的起始分隔符,后跟允许匹配且不包含起始分隔符的最短字符串,后跟一个空格,后跟等号,后跟允许匹配并以结束分隔符结尾的最短字符串。

2个回答

3
使用负向先行断言。类似这样的内容:
``` 使用负向先行断言。类似这样的内容: ```
my $variable = qr/ \G $start (?<variable> (.(?!$end))+ ) $end /xs;

也许我很蠢,但我尝试了这个 my $delimiters = qr/ \G \{\{ (?<start> .+?) = [ ] = (?<end> (?: . (?! }} ) )+? ) }} /xs;,但是 $delimiters 规则没有匹配(它最终进入了变量规则)。如果我正确理解逻辑的话,它应该是在说“匹配上次离开的位置,后跟 {{,然后是至少有一个字符允许匹配的最短字符串,后跟 "= =",然后是任何不跟随 }} 的字符一次或多次,后跟 }};然而,在 "{{(= =)}}" 中,)后面跟着 },那怎么可能行得通呢? - Chas. Owens
啊哈,我太蠢了,我需要使用 my $delimiters = qr/ \G \{\{ (?<start> (?: . (?! }} ) )+? ) = [ ] = (?<end> .+? ) }} /xs; 代替。这将防止第一个 .+? 跨越指令。谢谢。 - Chas. Owens
1
删除 (.(?!$end))+? 中的最后一个 ?,因为它后面跟着 $end - Qtax

2

对于那些感到好奇的人,以下是用Perl 5.10风格编写的Mustache完整标记解析器。现在我只需要编写解析器和渲染器。

#!/usr/bin/perl

use 5.010_000;
use strict;
use warnings;

sub gen_tokenizer {
    my ($s, $e) = @_;
    my ($start, $end) = map { quotemeta } $s, $e;

    my $unescaped = "$s $e" eq "{{ }}" ?
        qr/ \G \{{3}    (?<unescaped> .+?)  }{3} /xs :
        qr{ \G $start & (?<unescaped> .+? ) $end }xs;

    return qr{
        $unescaped                                                 |
        \G $start (?:
            !  (?<comment>    .+? )                                |
            >  (?<partial>    .+? )                                |
            \# (?<enum_start> .+? )                                |
            /  (?<enum_stop>  .+? )                                |
            (?<start> (?: . (?! $end ) )+? ) = [ ] = (?<end> .+? ) |
            (?<variable>      .+? )
        ) $end                                                     |
        (?<text> .+? ) (?= $start | \z )
    }xs; 
}

my $template  = do { local $/; <DATA> };
my $tokenizer = gen_tokenizer "{{", "}}";

while ($template =~ /$tokenizer/g) {
    my @types = keys %+;

    if (@types == 1) {
        my  $type     = $types[0];
        (my $contents = $+{$type}) =~ s/\n/\\n/g;

        say "$type: [$contents]";
    } else {
        $tokenizer = gen_tokenizer $+{start}, $+{end};

        say "set_delim: [$+{start} $+{end}]";
    }
}

__DATA__
{{!this is a comment}}
{{{html header}}}
Hi {{name}}, I like {{thing}}.
{{(= =)}}
(#optional)
This will only print if optional is set
(/optional)
(&html footer)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接