组合(OR)任意正则表达式

时间:2012-12-15 07:49:59

标签: java regex optimization

tl; dr 有没有办法在Java中将任意正则表达式组合成单个正则表达式(用于匹配,而不是捕获)?


在我的应用程序中,我收到了用户的两个列表:

  1. 正则表达式列表
  2. 字符串列表
  3. 我需要输出(2)中与(1)中的任何正则表达式都不匹配的字符串列表。

    我有明显的天真实现(遍历(2)中的所有字符串;对于每个字符串迭代(1)中的所有模式;如果没有模式匹配,则将字符串添加到将返回的列表中)但是我想知道是否有可能将所有模式组合成一个模式并让正则表达式编译器利用优化机会。

    OR组合正则表达式的显而易见的方法显然是(regex1)|(regex2)|(regex3)|...|(regexN)但是我很确定这不是正确的事情,因为我无法控制单个正则表达式(例如它们可能包含所有方式后退/前锋参考)。因此,我想知道你是否可以建议一种更好的方法来组合java中的任意正则表达式。


    注意:它只是由上面暗示的,但我会明确表示:我只是匹配字符串 - 我不需要使用捕获组的输出。

1 个答案:

答案 0 :(得分:4)

某些正则表达式引擎(例如PCRE)具有构造(?|...)。它就像一个非捕获组,但具有一个很好的特性,即每个交替组都从相同的初始值计算。这可能会立即解决您的问题。因此,如果为此任务切换语言是一个选项,那应该可以解决问题。

[编辑>实际上,它仍然会导致与命名捕获组发生冲突的问题。实际上,模式甚至不会编译,因为组名不能被重用。]

否则你将不得不操纵输入模式。 hyde建议对后向引用进行重新编号,但我认为有一个更简单的选项:将所有组命名为组。您可以向自己保证名称是唯一的。

基本上,对于每个输入模式,您都可以创建唯一标识符(例如,增加ID)。然后最棘手的部分是在模式中找到捕获组。您将无法使用正则表达式执行此操作。您必须自己解析模式。如果您只是遍历模式字符串,请注意以下内容:

  • 进入和离开字符类时请注意,因为字符内部的括号是文字字符。
  • 可能是最棘手的部分:忽略?:?=?!?<=?<!,{{1}后面的所有左括号}。此外,还有选项设置括号:?>(?idmsuxU-idmsuxU),它们也不会捕获任何内容(当然可能有这些选项的任何子集,它们可以按任何顺序排列 - (?idmsux-idmsux:somePatternHere)是也可选)。
  • 现在,您应该只留下正常捕获组或命名为-的左括号。最简单的方法可能是对它们进行相同的处理 - 即同时具有数字和名称(如果未设置,则名称等于数字)。然后你用(?<name>重写所有这些(连字符实际上不是名称的一部分,你只需要增加数字后跟哈希 - 因为哈希是固定长度的,所以不会有任何重复;至少相当)。请务必记住该组最初拥有的号码和名称。
  • 每当遇到反斜杠时,有三种选择:
    1. 下一个字符是数字。你有一个带编号的反向引用。将所有这些数字替换为(?<uniqueIdentifier-md5hashOfName>,其中k<name>是您为该组生成的新组名。
    2. 下一个字符为name。再次使用相应的新名称替换它。
    3. 下一个角色是其他任何东西。跳过它。它可以同时处理括号的转义和反斜杠的转义。
  • 我认为Java可能允许前向引用。在这种情况下,你需要两次通过。请先注意重命名所有组。然后更改所有引用。

在每个输入模式上完成此操作后,您可以安全地将所有输入模式与k<...>组合在一起。除反向引用之外的任何其他功能都不应该导致此方法出现问题。至少没有你的模式有效。当然,如果您有输入|a(b,那么您就遇到了问题。但是如果你不检查模式是否可以自己编译,那么你将永远拥有它。

我希望这能给你指向正确的方向。