Java 正则表达式匹配除可选后缀外的贪婪数据

Java regex matching greedy data apart from optional suffix

给定一个字符串,如

Prefix without commas, remainder with optional suffix (optional suffix)

什么是最好的 Java 正则表达式来一次性匹配和提取字符串的 3 个部分?

  1. 前缀到第一个逗号
  2. 左括号的余数
  3. 括号内的后缀

对于上面的例子,3 组(引号内)将是

  1. "Prefix without commas"
  2. "remainder with optional suffix"
  3. "(可选后缀)"

字符串的所有 3 个部分都是可变长度的。 "remainder" 部分本身可能包含逗号和圆括号,可选后缀可能以或不以 space(s) 开头,后跟左括号,后跟零个或多个字符,后跟右括号,再后跟通过可选的 spaces,然后是行尾。

尝试类似

([^,]*),(.*)(\s*\(.*\))?

只生成第 1 组和第 2 组,将第 3 组放在第 2 组的末尾。

([^,]*),(.*)(\s*\(.*\))?

失败的原因是正则表达式已经成功 ([^,]*),(.*) 并且不需要检查(回溯)其余部分。

要使其正常工作,请按如下方式更改(可能有多个选项),匹配时不带最后一个括号,或者匹配最后一个括号:

^([^,]*),(.*[^\) ]\s*$) | ([^,]*),(.*)(\s*\(.*\))\s*$

结果( + + 应该合并,</code>和<code>如果没有可选前缀则填充):

3: Prefix without commas
4:  remainder with optional suffix 
5: (optional suffix)

这里我假设你的可选后缀可以出现多次。阅读问题的另一种方式是您希望重复中间部分,即 </code> 包含在 <code> 中。您可以按如下方式进行:

^([^,]*),(.*(?:[^\) ]\s*$ | (\s*\(.*\)\s*$)))

结果:

1: Prefix without commas
2:  remainder with optional suffix (optional suffix)  
3: (optional suffix)  

编辑:更新上面的正则表达式以允许在右括号后使用白色space(这很微妙,您需要将 space 添加到否定字符 class),并且锚定正则表达式以加快速度并减少回溯

您可以使用以下正则表达式:

"^([^,]*),([^()]*)(\s*\(.*\))?$"

正则表达式匹配:

  • ^ - 字符串的开头
  • ([^,]*) -(第 1 组)除 ,
  • 之外的 0 个或更多字符
  • , - 文字 ,
  • ([^()]*) -(第 2 组)除 ()
  • 之外的 0 个或更多字符
  • (\s*\(.*\))? -(第 3 组)可选组(由于 ? 量词表示前面的子模式出现 1 次或 0 次):
    • \s* - 0 个或更多空格
    • \(.*\) - 文字 ( 然后尽可能多的字符,而不是换行符,直到最后一个 ).
  • $ - 字符串结尾(如果实际字符串可以更长,并且您正在寻找更小的子字符串,则删除)。

IDEONE demo

String str = "String prefix without commas, variable length remainder with optional suffix (optional suffix)";
Pattern ptrn = Pattern.compile("^([^,]*),([^()]*)(\s*\(.*\))?$");
Matcher matcher = ptrn.matcher(str);
while (matcher.find()) {
    System.out.println("First group: " + matcher.group(1)
                  + "\nSecond group: " + matcher.group(1) 
                  + (matcher.group(3) != null ? 
                       "\nThrid group: " + matcher.group(3) : ""));

以下正则表达式:

^([^,]*),(.*?)(?:\(([^()]*)\))?\s*$

在第 2 组中使用惰性量词来保证第 3 组在有括号时匹配。另一方面,第 3 组不允许嵌套括号,只在字符串的最后一组括号中强制匹配。

代码:

String text = "String prefix without commas, variable length ())(remainde()r with )optional (suffix (optional suffix)";
Pattern regex = Pattern.compile("^([^,]*),(.*?)(?:[(]([^()]*)[)])?\s*$");
Matcher m = regex.matcher(text);
if (m.find()) {
    System.out.println("1: " + m.group(1));
    System.out.println("2: " + m.group(2));
    System.out.println("3: " + m.group(3));
}

输出:

1: String prefix without commas
2:  variable length ())(remainde()r with )optional (suffix 
3: optional suffix

DEMO