代码之家  ›  专栏  ›  技术社区  ›  Clinton Pierce

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

  •  2
  • Clinton Pierce  · 技术社区  · 17 年前

    我选择了如下所示的文本。我需要对它做一个初步的编辑,但无法理解我需要的正则表达式。也许这是漫长的一天,我看不到我需要什么。

    样本数据:

    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
    

    CODE 和 ITEM BEGIN / END 节。幸运的是,这些部分使用 STOP START 开始 / 结束 (它们是关键词,不在其他任何地方)。

    我的工具箱是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;
    

    因为指示的代码匹配得太多,所以会在更远的地方找到正确的代码,但总是编辑第一个条目。

    建议?


    * 实际代码依赖于将正则表达式添加到一堆正则表达式(类似于后处理过滤器)上,每个正则表达式依次应用于文本进行编辑。见鬼,如果我有文本,我可以做一个完整的语法分析器。但我希望不必破坏代码,继续使用我拥有的API。

    3 回复  |  直到 17 年前
        1
  •  6
  •   JSBÕ±Õ¸Õ£Õ¹    17 年前

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

    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;
    }
    

    编辑: 如果您真的想要一个正则表达式,您可以使用如下内容(未经测试):

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

    增加 (?: (?! END ) . )*

        2
  •  4
  •   Axeman maxelost    17 年前

    虽然我不喜欢它有多大的回溯性,让大家都知道 贪婪的 之间 BEGIN 和 RATE 比率 CODE = XX . 这样地:

    $tx = qr/(START \s+ ITEM \s+ = \s+ 9983 \s+ 
                            BEGIN
                                .*
                               RATE \s+ = \s+ )\d+
    ...
    

    ITEM STOP . 像这样:

    my $tx = qr/(START \s+ ITEM \s+ = \s+ 9983 \s+
                     BEGIN
                         (?: (?! \b STOP \b ) . )*
                        RATE \s+ = \s+ )\d+
                             (.*?       # Goes too far
                         CODE \s+ = \s+ XX)
              /msx
              ;
    

    它仍然比我想象的要慢。

    (一小时后)我意识到 比率 代码 不能被一个 END

    my $tx = qr/(START \s+ ITEM \s+ = \s+ 9983 \s+
                     BEGIN
                         .*?
                        RATE \s+ = \s+ )\d+
                             ((?:(?! ^ \s+ END \s* $ ) . )*? 
                         CODE \s+ = \s+ XX)
                            /msx
                            ;
    

    (我修改了这个,只在一行中寻找结束。如果 ADDITIONAL TEXT 可能包含单端,则无论发生什么情况都很难解析)

    我想这一次不会倒退太多,因为它只是从 RATE = 然后扫描 CODE = 在它击中之前 结束 如果我们没有 CODE = XX ,然后修剪回它认为匹配的位置 然后去寻找下一个 比率 停止 如果我们不知道该项目#9983肯定会有“XX”代码。


    除假 \s 问题

    注:

    START ITEM = 9983
        BEGIN
            WORD
            RATE = 01
            MORE WORDS
            CODE = AA
            STUFF
        END
        BEGIN
            TEXT
            MORE WORDS
            RATE = 99
            ADDITIONAL TEXT <-- DON'T END HERE!
            CODE = XX
            OTHER THINGS
        END
    STOP
    
        3
  •  0
  •   David Harris    17 年前

    正则表达式并不总是解析文本的最佳答案。您的示例表明,您确实有一个可以用语法表示的文件。使用解析器提取字段,然后对提取的信息进行更新,这将简单得多。