【问题标题】:Regex: Determine if two regular expressions could match for the same input?正则表达式:确定两个正则表达式是否可以匹配相同的输入?
【发布时间】:2019-07-13 10:37:21
【问题描述】:

我想知道两个已知的正则表达式之间是否存在冲突,以便允许用户构造一个互斥正则表达式的列表。

例如,我们知道下面的正则表达式完全不同,但它们都匹配xy50

'^xy1\d'
'[^\d]\d2$'

是否有可能使用计算机算法来确定两个正则表达式是否会产生这样的冲突?怎么样?

【问题讨论】:

  • 我偷偷怀疑这个问题等同于停机问题。
  • @Seamus Campbell:太棒了!这是停止问题的味道吗?如果没有,那如何实现?
  • 另一种思考方式是......为什么不添加到用户的正则表达式中,从而使正则表达式互斥? IE。最后加上跟上一个不一样有帮助吗?
  • 我强烈怀疑这是否等同于停机问题。停机问题适用于图灵机,它位于乔姆斯基层次结构的顶部 (en.wikipedia.org/wiki/Chomsky_hierarchy)。正则表达式位于层次结构的底部。我怀疑这是一个可以解决的问题,虽然我不知道它是什么或如何解决。
  • 呃,你提供的两个正则表达式都不匹配xy50...

标签: regex


【解决方案1】:

这里不涉及停机问题。您只需要计算^xy1\d[^\d]\d2$ 的交集是否为非空。

我不能在这里给你一个算法,但这里有两个关于生成交集的方法的讨论,而无需求助于 DFA 的构造:

然后是拉格尔

也可以计算正则表达式的交集。

更新:我刚刚用 OP 的正则表达式试用了 Ragel。 Ragel 可以从生成的状态机中为 graphviz 生成一个“点”文件,这非常棒。 OP 正则表达式的交集在 Ragel 语法中如下所示:

('xy1' digit any*) & (any* ^digit digit '2') 

并具有以下状态机:

而空的路口:

('xy1' digit any*) & ('q' any* ^digit digit '2')

看起来像这样:

所以如果其他方法都失败了,那么您仍然可以让 Ragel 计算交集,并通过比较生成的“点”文件来检查它是否输出空状态机。

【讨论】:

  • 添加这个有点晚了,但是一个简单的证明是这样的:在你添加第二个与第一个的相交补码后,你可以通过尝试所有来测试它是否非空字母表中的字母组合最多与您的 FSM 中的状态一样多的字符。其余的只是抽水引理,因为 FSM 中的状态数是抽水极限的简单上限。
【解决方案2】:

这个问题可以重述为,“做两个或多个常规描述的语言 表达式有一个非空的交集”?

如果您将问题限制为 正则表达式(无反向引用、前瞻、 向后看,或其他允许识别上下文无关或更复杂的功能 语言),这个问题至少是可以确定的。常规语言在 交集,并且有一个算法采用两个正则表达式 作为输入并在有限时间内产生一个识别交叉点的 DFA。

每个正则表达式都可以转换为不确定的有限自动机, 然后到确定性有限自动机。可以转换一对 DFA 到识别交叉点的 DFA。如果有一条从 开始状态到最终 DFA 的任何接受状态,交集非空 (“冲突”,使用您的术语)。

不幸的是,在转换初始 NFA 时可能会出现指数级爆炸 到 DFA,因此问题在实践中很快变得不可行,因为 输入表达式增长。

如果允许对纯正则表达式进行扩展,那么所有的赌注都没有了—— 这样的语言在交集下不再封闭,所以这种结构不会 工作。

【讨论】:

    【解决方案3】:

    是的,我认为这是可以解决的:除了将正则表达式视为匹配字符串之外,您还可以将它们视为生成字符串。也就是说,它们匹配的所有字符串。

    让 [R] 是由正则表达式 R 生成的字符串集合。然后给定正则表达式 R 和 T,我们可以尝试将 T 与 [R] 匹配。即 [R] 匹配 T 当且仅当 [R] 中有一个字符串匹配 T。

    应该可以将其开发成一种算法,其中 [R] 是根据需要延迟构造的:正常的正则表达式匹配会尝试匹配输入字符串中的下一个字符,然后前进到字符串中的下一个字符,修改后的算法将检查与输入正则表达式对应的 FSM 是否可以在其当前状态生成匹配字符,然后同时前进到“所有下一个状态”。

    【讨论】:

    • 我不太明白。但是话又说回来,他们将匹配的字符串列表可能是无限的......此外,尝试将 T 与 [R] 匹配是这里的关键点。恕我直言,您的算法需要更好地定义。
    【解决方案4】:

    另一种方法是改用 Dan Kogai 的 Perl Regexp::Optimizer

      use Regexp::Optimizer;
      my $o  = Regexp::Optimizer->new->optimize(qr/foobar|fooxar|foozap/);
      # $re is now qr/foo(?:[bx]ar|zap)/
    

    .. 首先,优化然后丢弃所有冗余模式。

    也许 Ron Savage 的 Regexp::Assemble 会更有帮助。 它允许将任意数量的正则表达式组合成一个正则表达式,以匹配所有单个 RE 匹配的内容。* 或两者的组合。

    * 但是,您需要注意 Perl 和 Java 或其他 PCRE 风格之间的一些差异。

    【讨论】:

      【解决方案5】:

      如果您正在寻找 Java 中的库,您可以使用 Automaton 使用 '&' 运算符:

      RegExp re = new RegExp("(ABC_123.*56.txt)&(ABC_12.*456.*\\.txt)", RegExp.INTERSECTION); // Parse RegExp
          Automaton a = re.toAutomaton(); // convert RegExp to automaton
      
          if(a.isEmpty()) { // Test if intersection is empty
            System.out.println("Intersection is empty!");
          }
          else {
            // Print the shortest accepted string
            System.out.println("Intersection is non-empty, example: " + a.getShortestExample(true));
          }
      

      原答案:

      Detecting if two regexes could possibly match the same string

      【讨论】:

        猜你喜欢
        • 2021-10-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多