如何订购正则表达式替代品以获得最长匹配?

时间:2016-03-14 20:25:24

标签: regex language-agnostic

我将一些正则表达式regex1regex2,...,regexN合并为一个正则表达式regex1|regex2|...|regexN。我想重新排序组件表达式,以便组合表达式在给定字符串的开头给出最长的匹配。

我认为这意味着重新排序正则表达式,以便“如果regexK匹配前缀regexL,那么L < K”。如果这是正确的,通常是否可以找出regexK是否可以匹配前缀regexL

3 个答案:

答案 0 :(得分:7)

使用正确的正则表达式!

在一些正则表达式中,提供最长匹配的交替是使用的交替(“贪婪交替”)。请注意,大多数这些正则表达式都是旧的(现在仍然使用),因此缺少一些现代结构,例如后向引用。

Perl6是现代的(and has many features),但默认为POSIX风格的最长交替。 (您甚至可以切换样式,因为||会创建一个与首次匹配短路的交流发电机。)请注意,需要:Perl5/:P5修饰符才能使用“传统”正则表达式样式。

此外,PCRE和较新的PCRE2具有相同的功能。在PCRE2中,它是pcre2_dfa_match。 (有关DFA的更多信息,请参阅我的有关正则表达式引擎设计的相关信息部分。)

这意味着,您可以在管道中拥有任何语句顺序,结果将始终最长。

(这与“绝对最长”匹配不同,因为在交替中重新排列术语的数量不会改变所有正则表达式引擎从左到右遍历字符串的事实。除了.NET,显然,它可以从右向左。但是向后遍历字符串也不能保证“绝对最长”的匹配。)如果真的希望在(仅)开头找到匹配字符串,你应该锚定表达式:^(regex1|regex2|...)

根据this page*:

  

然而,POSIX标准要求返回最长匹配。将Set|SetValue应用于SetValue时,符合POSIX标准的正则表达式引擎将完全匹配SetValue

*注意:我无法测试每个 POSIX风格。此外,一些正则表达式风格(Perl6)具有此行为,而不是总体上符合POSIX。

让我举一个我在自己的计算机上验证的具体示例:

echo "ab c a" | sed -E 's/(a|ab)/replacement/'

正则表达式为(a|ab)。当它在字符串ab c a上运行时,您得到:replacement c a,这意味着您实际上可以获得交流发电机可以提供的最长匹配

对于更复杂的示例(a|ab.*c|.{0,2}c*d)应用于abcccd,此正则表达式将返回abcccd

Try it here!

更多说明:正则表达式引擎不会继续(在搜索字符串中),以便在匹配某些内容后查看是否存在更长的匹配。它只会查看当前的更改列表,以查看另一个更改是否匹配更长的字符串(从初始匹配开始的位置)。

换句话说,无论修改中的选项的顺序,POSIX兼容的正则表达式都使用与大多数字符匹配的正则表达式。

具有此行为的其他风味示例:

  • Tcl ARE
  • POSIX ERE
  • GNU BRE
  • GNU ERE

有关正则表达式引擎设计的相关信息

This question询问设计引擎,但答案可能有助于理解这些引擎的工作原理。基本上,基于DFA的算法确定不同表达式的共同重叠,,尤其是交替中的那些。可能值得查看this page。它解释了如何将替代方案组合到一条路径中: Thompson algorithm for alternation]]

注意:在某些时候,您可能只想考虑使用实际的编程语言。正则表达并非一切。

答案 1 :(得分:6)

Longest Match

不幸的是,没有不同的逻辑来告诉正则表达式
引擎可以获得最长的匹配。

这样做会/可能会产生一个疯狂的级联回溯事件 根据定义,它的复杂性太大而无法处理。

所有正则表达式都是从左到右处理的 引擎可以匹配首先的任何东西,然后纾困。

对于this|this is|this is here的替换,尤其如此 将始终匹配'this首先在这里'和 将从不匹配this isthis is here

一旦你意识到这一点,你可以将交替重新排序为
this is here|this is|this每次给出最长的匹配。

当然这可以减少到this(?:(?: is)? here)?
这是获得最长比赛的聪明方式。

没有看到任何你要组合的正则表达式的例子,
所以这只是一些一般信息。

如果您显示正在尝试合并的正则表达式,则可以使用更好的解决方案 提供。

替代内容相互影响,以及前面或后面的内容 跟随群集会影响哪个轮流匹配。

如果您有更多问题,请询问。

附录:

@Laurel。这总是可以用Perl 5正则表达式(&gt; 5.10)来完成 因为Perl可以在正则表达式子表达式中运行代码 由于它可以运行代码,因此可以计算并获得最长的匹配。

然而,最左边的规则永远不会改变 如果正则表达式是热力学,那么这将是第一定律。

Perl是一个奇怪的实体,因为它试图在正则表达式之间创建协同作用 和代码执行。

因此,可以使其操作员超载,以便注入 定制语言本身。
他们的正则表达式引擎没有什么不同,可以以相同的方式定制。

因此,从理论上讲,下面的正则表达式可以构成一个正则表达式构造,
一个新的 Alternation 构造。

我不会在这里详细介绍,但足以说明,它不适合胆小的人。
如果您对此类事物感兴趣,请参阅
下的 perlre 联机帮助页 部分'创建自定义RE引擎'

Perl:

注 - 正则表达式替换表单基于@Laurel complex 示例
(a|ab.*c|.{0,2}c*d)已应用于abcccd

在视觉上,如果制作自定义正则表达式构造,看起来类似于
交替(?:rx1||rx2||rx3),我猜这是多少 Perl6是将regex引擎直接集成到语言中的。

此外,如果按原样使用,则可以根据需要动态构造此正则表达式。
请注意,Perl正则表达式构造的所有丰富性都可用。

输出

Longest Match Found:  abcccd

代码

use strict;
use warnings;

my ($p1,$p2,$p3) = (0,0,0);
my $targ = 'abcccd';

# Formatted using RegexFormat7 (www.regexformat.com)

if ( $targ =~
/
   # The Alternation Construct
     (?=
          ( a )                         # (1)
          (?{ $p1 = length($^N) })
     )?
     (?=
          ( ab .* c )                   # (2)
          (?{ $p2 = length($^N) })
     )?
     (?=
          ( .{0,2} c*d )                # (3)
          (?{ $p3 = length($^N) })
     )?
   # Check At Least 1 Match
     (?(1)
          (?(2)
               (?(3)
                 |  (?!)
               )
          )
     )
   # Consume Longest Alternation Match
     (                                  # (4 start)
          (?(?{
               $p1>=$p2 && $p1>=$p3
            })
               \1 
            |  (?(?{
                    $p2>=$p1 && $p2>=$p3
                 })
                    \2 
                 |  (?(?{
                         $p3>=$p1 && $p3>=$p2
                      })
                         \3 
                    )
               )
          )
     )                                  # (4 end)
/x ) {

    print "Longest Match Found:  $4\n";
} else {
    print "Did not find a match!\n";
}

答案 2 :(得分:2)

对于某些情况,确定一个人可能能够判断两个给定的正则表达式是否匹配前缀。通常,这是一个n-p完全问题。所以不要试试。

在最好的情况下,将不同的正则表达式组合成单个正则会给出合适的结果。但是,我不知道任何算法可以采用两个任意的正则表达式并将它们组合起来,使得结果正则表达式仍然匹配任何两个匹配的正则表达式。它也将是n-p-complete。

您还必须不依赖于替代品的订购。这取决于正则表达式引擎的内部执行逻辑。可能很容易就是在内部重新排序替代方案,超出了您的控制范围。因此,使用当前发动机mmight的有效排序会因不同的发动机而产生错误的结果。 (因此,只要您使用单个正则表达式引擎实现,它就可以提供帮助)

在我看来,最好的办法是简单地执行所有正则表达式,跟踪匹配的长度,然后进行最长的匹配。