【问题标题】:regex optional capture not working as expected正则表达式可选捕获未按预期工作
【发布时间】:2016-11-08 13:29:47
【问题描述】:

我需要像这样捕获 所有 字符串的元素

front stuff grp2="abc" middle stuff grp4="xyz" end stuff

这样分成这五组

#1: front stuff
#2: grp2="abc"
#3: middle stuff
#4: grp4="xyz"
#5: end stuff

只要五个部分都存在,这个表达式就可以解决问题

([\s\S]*?)(grp2=\"\S*?\")([\s\S]*?)(grp4=\"\S*?\")([\s\S]*)

但如果 grp4="..." 不存在,例如,

front stuff grp2="abc" end stuff

当然完全不匹配。

好吧,我可以像这样将第 4 组设为可选,对吧?

([\s\S]*?)(grp2=\"\S*?\")([\s\S]*?)(grp4=\"\S*?\")?([\s\S]*)

显然错了。产生的是这个(当 grp4 存在时)

#1: front stuff
#2: grp2="abc"
#3: 
#4: 
#5: middle stuff grp4="xyz" end stuff

第 4 组即使存在也不再匹配。

FWIW,我需要所有文本(所有组都必须是捕获组),因为我最终使用它来操作组 2 和 4(如果它们存在)的文本,并重构字符串。就像拿那个示例字符串并将它变成这个

front stuff grp2="123" middle stuff grp4="456" end stuff

在 regex101.com 上很容易看到这种行为。我已经尝试了我所知道的所有“可选”组合。我确定我一定是在做一些愚蠢的事情,而且我已经浪费了足够多的时间来解决这个问题,所以我终于寻求帮助了。

谢谢!

【问题讨论】:

  • Java 和 Javascript 是不同的语言,而且很可能有稍微不同的正则表达式方言。你真正对哪个感兴趣?
  • 当您的第三个捕获组第一次测试时,它会针对空字符串进行测试,因为您使用了非贪婪运算符(它会尝试最小的匹配,并在每次匹配时逐渐增加尝试匹配的长度回溯)。这使得第四个捕获组也可以匹配空字符串(可选匹配),然后第五个捕获组捕获字符串的其余部分。无论grp4="..." 是否出现在字符串中,情况总是如此。
  • @JonSkeet 我同意 java 和 javascript 是不同的,但是,在这种情况下,我认为表达式是相同的(Java 中的额外转义除外)。如果解决方案不同,我确实需要用两种语言来做这件事。为了简单起见,我在测试用例中使用了 javascript 语法。
  • @danno57:您假设 Java 和 Javascript 中的正则表达式是相同的。我的经验是,正则表达式的各种方言之间存在细微的差异——除此之外,你的问题并没有说明它们……

标签: javascript java regex


【解决方案1】:

您可以将中间的middle stuffgrp4 设为可选,因为两者都有end stuff。您的新正则表达式将是 ([\\s\\S]*?)(grp2=\"\\S*?\")(?:([\\s\\S]*?)(grp4=\"\\S*?\")){0,1}([\\s\\S]*)

String test = "front stuff grp2=\"abc\" middle stuff grp4=\"xyz\" end stuff";
Pattern p = Pattern.compile("([\\s\\S]*?)(grp2=\"\\S*?\")(?:([\\s\\S]*?)(grp4=\"\\S*?\")){0,1}([\\s\\S]*)");
Matcher m = p.matcher(test);

for(int i=1; i<=m.groupCount(); i++) {
    if(m.group(i)!=null) {
        System.out.println(i+": "+m.group(i));
    }
}
// String test = "front stuff grp2=\"abc\" middle stuff grp4=\"xyz\" end stuff";
// 1: front stuff 
// 2: grp2="abc"
// 3:  middle stuff 
// 4: grp4="xyz"
// 5:  end stuff

// String test = "front stuff grp2=\"abc\" end stuff";
// 1: front stuff 
// 2: grp2="abc"
// 3:  end stuff

【讨论】:

  • 是的,这似乎有效!我在第 4 组之后尝试过 {0,1},但没有想过对第 3 组做任何事情。谢谢!
【解决方案2】:

正则表达式中的 | 使您能够在掩码上设置 OR 选项。

您可以使用这种公式: ([\s\S]?)(grp2=\"\S?\")([\s\S]?)(grp4=\"\S ?\")([\s\S])|([\s\S]?)(grp2=\"\S*?\")([ \s\S])|([\s\S])(grp4=\"\S*?\")([\s\S]*)

即使 grp2 不存在或 grp4 不存在或两者都存在,它也可以工作。

希望对您有所帮助。

【讨论】:

  • 是的,但它不能满足我捕获所有组的要求。它正确匹配 grp2 和/或 grp4 (如果两者都存在,则为 2 个匹配项,如您所说),但丢弃所有其他“东西”,我需要这些“东西”以便能够用组 2 和/或 4 重构原始字符串修改。
猜你喜欢
  • 1970-01-01
  • 2014-04-28
  • 2021-01-14
  • 2022-01-02
  • 1970-01-01
相关资源
最近更新 更多