如何在两个已知令牌之间进行最小匹配?

Cli*_*rce 2 regex perl

我有一些文本,如下所示.我需要对它做一个基本的编辑,但无法理解我需要的正则表达式.也许这只是漫长的一天,我没有看到我需要的东西.

样本数据:

START ITEM = 1235
    BEGIN
        WORD
        RATE = 98
        MORE WORDS
        CODE = XX
        STUFF
    END
    BEGIN
        TEXT
        MORE WORDS
        RATE = 57
        ADDITIONAL TEXT
        CODE = YY
        OTHER THINGS
    END
STOP
START ITEM = 9983
    BEGIN
        WORD
        RATE = 01
        MORE WORDS
        CODE = AA
        STUFF
    END
    BEGIN
        TEXT
        MORE WORDS
        RATE = 99
        ADDITIONAL TEXT
        CODE = XX
        OTHER THINGS
    END
STOP
Run Code Online (Sandbox Code Playgroud)

我给了一个CODE和一个ITEM数字,需要在相应的BEGIN/ END部分编辑速率.幸运的是,这些部分定义为STOP/ START BEGIN/ END(它们是关键字,而不是其他任何地方).

我的工具箱是Perl正则表达式.*

我尝试的第一个解决方案不起作用(值硬编码):

    $tx =~ s/(START \s ITEM \s = \s 9983.*?
                            BEGIN
                                .*?
                               RATE \s = \s )\d+
                                    (.*?       # Goes too far
                                CODE \s = \s XX)
                        /$1$newRate$2
                        /sx;
Run Code Online (Sandbox Code Playgroud)

因为指示的代码过多地匹配,找到更正确的代码但总是编辑第一个条目.

建议?


*实际代码依赖于正则表达式被添加到一堆正则表达式(一种后处理过滤器)上,每个正则表达式依次应用于文本进行编辑.哎呀,如果我有文字,我可以做一个全功能的解析器.但是我希望不要打破这个代码并坚持使用我已经拥有的API.

JSB*_*ոգչ 6

正则表达式不适合这类问题.我推荐一个简单的迭代解决方案:

while (<FILE>) {
    # push lines straight to output until we find the START that we want
    print OUT $_;
    next unless m/START ITEM = $number/;

    # save the lines until we get to the CODE that we want
    my @lines;
    while (<FILE>)
    {
        push @lines, $_;
        last if m/CODE = $code/;
    }

    # @lines now has everything from the START to the CODE. Get the last RATE in
    # @lines and change its value.
    my $strref = \( grep m/RATE/ @lines )[-1];
    $$strref = $new_value;

    # print out the lines we saved and exit the loop
    print OUT @lines;
    last;
}
Run Code Online (Sandbox Code Playgroud)

编辑:如果你真的想要一个正则表达式,你可以使用这样的东西(未经测试):

$tx =~ s/(START \s+ ITEM \s+ = \s+ 9983.*?
                            BEGIN
                                .*?
                               RATE \s+ = \s+ )\d+
                                ( (?: (?! END ) . )*
                                    CODE \s+ = \s+ XX)
                        /$1$newRate$2
                        /sx;
Run Code Online (Sandbox Code Playgroud)

添加的(?: (?! END ) . )*确保RATE和CODE之间的匹配不会越过END.但这将比非正则表达式方法慢得多.

  • 听起来像clintp有一个DailyWTF条目. (2认同)