Java正则表达式:将重复的子模式“分解”

4
我是一款商业闭源的Java应用程序的用户,除了它所提供的所有功能外,还可以通过提供正则表达式模式字符串来过滤文本字段。我经常广泛使用这个过滤功能。
我的问题是,我经常发现自己在正则表达式中重复相同的子模式。例如,在这里:
^(
    ( # pattern foo
        foo_([^_]+)_(windows|linux|osx)
    )
    |
    ( # pattern bar
        ([^_]+)_bar_(windows|linux|osx)_foo_(windows|linux|osx)
    )
)$

([^_]+)(windows|linux|osx)这部分经常重复出现。

这只是一个虚构的例子。原始的正则表达式更加复杂,大约大20倍,并且有许多不同的重复。由于重复的子模式大小和数量都在增加,因此它变得有点难以阅读,并且当您尝试修改重复的子模式时,必须同时修改所有重复的子模式,这很麻烦。

所以,我使用regex101进行了一些尝试,并得出了以下结果:

^(
    ( # a dummy option, defines some frequently used capture groups
        (?!x)x # always false, so nothing matches this and the following groups ever
        (?'name'[^_]+) # group "name"
        (?'os'windows|linux|osx) # group "os"
    )
    |
    ( # pattern foo
        foo_\g'name'_\g'os'
    )
    |
    ( # pattern bar
        \g'name'_bar_\g'os'_foo_\g'os'
    )
)$

regex101 save

的意思是“保存到regex101网站”。

现在所有子模式都有名称,每当我引用名称时,它们就会被替换为子模式字符串(例如\g'os'被替换为(windows|linux|osx))。名称比相应的子模式短得多,它们也很清晰,只需修改一次子模式即可将修改应用于正则表达式中的所有位置。

这个改进版本的问题在于虽然它是有效的PHP pcre正则表达式,但它是无效的Java正则表达式。除了正则表达式中的注释和错误行之外,Java不支持\g,如Comparison to Perl 5所述。

在Java Regex中是否有任何方法可以“因式分解”重复的正则表达式模式?别忘了我能做的只是提供一个模式字符串,我无法访问代码。


1
https://dev59.com/I3RC5IYBdhLWcg3wFNDX#415635 - Jorge Campos
1
@CookieCat:你想做的事情可以通过Java中的字符串拼接来实现。一个例子:http://stackoverflow.com/questions/26507391/java-regular-expression-for-detecting-class-interface-etc-declaration/26513446#26513446(向下滑动到底部) - nhahtdh
1
@nhahtdh 这是正确的,但我在问题的开头就提到了我是商业闭源Java应用程序的用户,并在问题的最后重申了这一点,说我无法访问它的源代码。我需要完全使用Java的正则表达式来完成所有任务。其他类型的正则表达式(例如Perl,Python,JavaScript,PHP等)支持\g转义序列来引用命名组,这就是可以解决我的问题的方法,但是Java不支持。我的问题是:我想在Java的正则表达式中实现的内容是否可能? - Cookie Cat
@CookieCat:那么根据Java本身的规则,你想实现的功能是不可能的。 - nhahtdh
1
@nhahtdh 我明白了。我希望可能会有一些巧妙的解决方法。尽可能地保持仅使用正则表达式是更加理想的,但既然无法避免,我将不得不编写一个程序,使用变量来进行所需替换,并将所需的正则表达式打印到标准输出。 - Cookie Cat
显示剩余4条评论
3个回答

0

从Java 8开始,纯正则表达式解决方案不存在。未来的新版本可能会支持\g

如前所述,唯一的解决方案是字符串连接技术。但在您的情况下不是一个选项。

如果您告诉我们商业闭源Java应用程序的名称,也许我们可以更好地帮助您。


0
如果您可以在提交模式之前运行一些Java代码,那么您可以使用apache.commons中的StrSubstitutor:
Map<String, String> valuesMap = new HashMap<>();
valuesMap.put("os", "(windows|linux|osx)");
valuesMap.put("name", "(?[^_]+)");
StrSubstitutor sub = new StrSubstitutor(valuesMap);

String template ="^(\n"+
        "    ( # pattern foo\n"+
        "        foo_${name}_${os}\n"+
        "    )\n"+
        "    |\n"+
        "    ( # pattern bar\n"+
        "        ${name}_bar_${os}_foo_${os}\n"+
        "    )\n"+
        ")$";
String regex = sub.replace(template);
System.out.println(regex);

0

你的正则表达式简化为^(?:foo_[^_]+|[^_]+_bar_(?:windows|(?:linu|os)x)_foo)_(?:windows|(?:linu|os)x)$

^ 
(?:
  foo_ [^_]+ 
| [^_]+ _bar_
  (?:
    windows
  | (?: linu | os )
    x
  )
  _foo
)
_
(?:
  windows
| (?: linu | os )
  x
)
$

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接