6

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


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

  1. 正则表达式列表
  2. 字符串列表

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

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

显然,OR-combine regex 的明显方法是,(regex1)|(regex2)|(regex3)|...|(regexN)但我很确定这不是正确的做法,因为我无法控制各个 regex(例如,它们可以包含所有向后/向前引用的方式)。因此,我想知道您是否可以建议一种更好的方法来组合 java 中的任意正则表达式。


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

4

1 回答 1

4

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

[编辑:事实上,它仍然会导致命名捕获组冲突的问题。事实上,该模式甚至无法编译,因为组名不能被重用。]

否则,您将不得不操纵输入模式。hyde 建议重新编号反向引用,但我认为有一个更简单的选择:将所有组命名为组。您可以向自己保证,这些名称是独一无二的。

所以基本上,对于每个输入模式,您都会创建一个唯一标识符(例如,增加一个 ID)。然后最棘手的部分是在模式中找到捕获组。您将无法使用正则表达式执行此操作。您必须自己解析模式。如果您只是简单地遍历模式字符串,这里有一些关于要注意什么的想法:

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

一旦对每个输入模式都完成了此操作,您就可以安全地将所有输入模式与|. 除了反向引用之外的任何其他功能都不应导致此方法出现问题。至少只要你的模式是有效的。当然,如果你有输入a(bc)d那么你就有问题了。但是,如果您不检查模式是否可以自行编译,您将始终拥有它。

我希望这可以为您指明正确的方向。

于 2012-12-15T22:57:04.057 回答