使用相同的组捕获<thisPartOnly>和(thisPartOnly)

pol*_*nts 4 java regex capturing-group

假设我们有以下输入:

<amy>
(bob)
<carol)
(dean>
Run Code Online (Sandbox Code Playgroud)

我们还有以下正则表达式:

<(\w+)>|\((\w+)\)
Run Code Online (Sandbox Code Playgroud)

现在我们得到两场比赛(如rubular.com上所示):

  • <amy>是匹配,\1捕获amy,\2失败
  • (bob)是匹配,\2捕获bob,\1失败

这个正则表达式完成了我们想要的大部分内容,它们是:

  • 它恰当地匹配打开和关闭括号(即没有混合)
  • 它捕获了我们感兴趣的部分

但是,它确实有一些缺点:

  • 重复捕获模式(即"主要"部分)
    • 这只是\w+在这种情况下,但一般来说这可能非常复杂,
      • 如果涉及反向引用,则必须为每个备用重新编号!
      • 重复使维护成为一场噩梦!(如果它改变了怎么办?)
  • 这些组基本上是重复的
    • 根据哪些备用匹配,我们必须查询不同的组
      • 它只是\1\2在这种情况下,但通常"主要"部分可以拥有自己的捕获组!
    • 这不仅不方便,而且可能存在不可行的情况(例如,当我们使用仅限于查询一个组的自定义正则表达式框架时)
  • 如果我们也想要匹配等{...},情况会迅速恶化[...].

所以问题很明显:如何在不重复"主要"模式的情况下做到这一点?

注意:在很大程度上我对java.util.regex口味感兴趣,但欢迎其他口味.


附录

这部分没有什么新内容; 它只用一个例子说明了上面提到的问题.

让我们将上面的例子带到下一步:我们现在想要匹配这些:

<amy=amy>
(bob=bob)
[carol=carol]
Run Code Online (Sandbox Code Playgroud)

但不是这些:

<amy=amy)   # non-matching bracket
<amy=bob>   # left hand side not equal to right hand side
Run Code Online (Sandbox Code Playgroud)

使用替代技术,我们有以下工作(如rubular.com上所示):

<((\w+)=\2)>|\(((\w+)=\4)\)|\[((\w+)=\6)\]
Run Code Online (Sandbox Code Playgroud)

如上所述:

  • 主要模式不能简单地重复; 反向引用必须重新编号
  • 如果改变,重复也意味着维护噩梦
  • 根据所替换的比赛,我们必须查询要么\1 \2,\3 \4\5 \6

Ala*_*ore 5

在进行真正的匹配之前,您可以使用前瞻"锁定"组号.

String s = "<amy=amy>(bob=bob)[carol=carol]";
Pattern p = Pattern.compile(
  "(?=[<(\\[]((\\w+)=\\2))(?:<\\1>|\\(\\1\\)|\\[\\1\\])");
Matcher m = p.matcher(s);

while(m.find())
{
  System.out.printf("found %s in %s%n", m.group(2), m.group());
}
Run Code Online (Sandbox Code Playgroud)

输出:

found amy in <amy=amy>
found bob in (bob=bob)
found carol in [carol=carol]
Run Code Online (Sandbox Code Playgroud)

它仍然很难看,但每次进行更改时都不必重新计算所有组号.例如,要添加对大括号的支持,它只是:

"(?=[<(\\[{]((\\w+)=\\2))(?:<\\1>|\\(\\1\\)|\\[\\1\\]|\\{\\1\\})"
Run Code Online (Sandbox Code Playgroud)