【问题标题】:Java Regex: "factor out" repeated subpatternsJava Regex:“分解”重复的子模式
【发布时间】:2015-11-07 04:19:12
【问题描述】:

我正在使用一个商业闭源 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

现在所有子模式都被命名了,每当我引用该名称时,它们都会被子模式字符串替换(即\g'os'(windows|linux|osx) 替换)。名称比相应的子模式短很多,它们也很清楚,您必须修改一次子模式才能使修改应用于正则表达式中的所有位置。

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

有什么方法可以“分解”像 Java 正则表达式中那样的重复正则表达式模式?别忘了,我能做的只是提供一个模式字符串,我无法访问代码。

【问题讨论】:

  • 它丝毫没有回答我的问题。它说支持命名组和使用\k,但仍然不支持我需要的\g
  • @CookieCat:你想做的事情可以通过Java中的字符串连接来实现。一个例子:stackoverflow.com/questions/26507391/…(向下滚动到底部)
  • @nhahtdh 是正确的,除了我在问题的开头提到我是商业闭源 Java 应用程序的用户并在我的问题的最后重申它说我无权访问它的源代码。我需要完全在 Java 的 Regex 中完成所有工作。其他风格的正则表达式,例如 Perl、Python、JavaScript、PHP 和许多其他支持 \g 转义序列来引用命名组,这可以解决我的问题,但 Java 不支持它。我的问题是我想要的是否可以在 Java 的 Regex 中完成。
  • @nhahtdh 我明白了。我希望可能有一些聪明的解决方法。尽可能多地保持正则表达式更为可取,但由于无法绕过它,我将不得不求助于编写一个程序,将我想要的正则表达式打印到标准输出,使用变量进行替换我想要。

标签: java regex


【解决方案1】:

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

如前所述,唯一的解决方案是字符串连接技术。但是,这不是您的选择。

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

【讨论】:

    【解决方案2】:

    如果您可以在提交模式之前运行一些 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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-28
      • 2013-07-01
      相关资源
      最近更新 更多