Java正则表达式匹配器无法找到所有可能的匹配。

3

我在TutorialsPoint看到一段代码,有些地方让我感到困惑…请看下面的代码:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexMatches
{
    public static void main( String args[] ){

      // String to be scanned to find the pattern.
      String line = "This order was placed for QT3000! OK?";
      String pattern = "(.*)(\\d+)(.*)";

      // Create a Pattern object
      Pattern r = Pattern.compile(pattern);

      // Now create matcher object.
      Matcher m = r.matcher(line);
      while(m.find( )) {
         System.out.println("Found value: " + m.group(1));
         System.out.println("Found value: " + m.group(2));
         System.out.println("Found value: " + m.group(3));
      }
   }
}

这段代码成功地打印出了:

Found value: This was placed for QT300 
Found value: 0
Found value: ! OK?

但是根据正则表达式"(.*)(\\d+)(.*)" ,为什么它不会返回其他可能的结果,例如:

Found value: This was placed for QT30 
Found value: 00
Found value: ! OK?

或者
Found value: This was placed for QT 
Found value: 3000
Found value: ! OK?

如果这段代码无法完成此操作,则我该如何编写一个可以查找所有可能匹配项的代码?

2个回答

5
因为*贪婪性,才会出现回溯
字符串:
This order was placed for QT3000! OK?

正则表达式:
(.*)(\\d+)(.*)

我们都知道.*是贪婪的,会尽可能地匹配所有字符。因此,第一个.*匹配到最后一个字符?之前的所有字符,然后按顺序回溯以提供匹配。我们正则表达式中的下一个模式是\d+,所以它回溯到一位数字。一旦找到一个数字,\d+就会匹配该数字,因为此处满足条件(\d+匹配一个或多个数字)。现在,第一个(.*)捕获了This order was placed for QT300,接下来的(\\d+)捕获了紧挨着!符号的数字0

现在,下一个模式(.*)捕获了剩余的所有字符,即!<space>OK?m.group(1)指的是位于组索引1内的字符,m.group(2)指的是索引2,依此类推。

请参见演示here

获取您想要的输出。
String line = "This order was placed for QT3000! OK?";
  String pattern = "(.*)(\\d{2})(.*)";

  // Create a Pattern object
  Pattern r = Pattern.compile(pattern);

  // Now create matcher object.
  Matcher m = r.matcher(line);
  while(m.find( )) {
     System.out.println("Found value: " + m.group(1));
     System.out.println("Found value: " + m.group(2));
     System.out.println("Found value: " + m.group(3));
  }

输出:

Found value: This order was placed for QT30
Found value: 00
Found value: ! OK?

(.*)(\\d{2}),回溯两个数字以提供匹配。

将您的模式更改为此:

String pattern = "(.*?)(\\d+)(.*)";

要获得如下输出,

Found value: This order was placed for QT
Found value: 3000
Found value: ! OK?

* 后加上 ? 可以强制 * 进行非贪婪匹配。
使用额外的捕获组可以从单个程序中获取输出。
String line = "This order was placed for QT3000! OK?";
String pattern = "((.*?)(\\d{2}))(?:(\\d{2})(.*))";
Pattern r = Pattern.compile(pattern);
      Matcher m = r.matcher(line);
      while(m.find( )) {
         System.out.println("Found value: " + m.group(1));
         System.out.println("Found value: " + m.group(4));
         System.out.println("Found value: " + m.group(5));
         System.out.println("Found value: " + m.group(2));
         System.out.println("Found value: " + m.group(3) + m.group(4));
         System.out.println("Found value: " + m.group(5));
     }

输出:

Found value: This order was placed for QT30
Found value: 00
Found value: ! OK?
Found value: This order was placed for QT
Found value: 3000
Found value: ! OK?

谢谢...但这只返回了一个匹配项...如果我想要返回所有的匹配项呢? - Cypher
好的...这很不错!我按照你说的做了,也得到了你的答案...但我的问题仍然是为什么只找到了一个匹配?使用你的代码只会给出(This order was placed for QT/3000/ ! OK),然后循环就结束了...我怎样才能同时获得其他答案,比如(This order was placed for QT3/000/ !OK)或者(This order was placed for QT300/0/! OK?)或者(This order was placed for QT30/00/! OK?)? 现在循环只运行一次,然后就结束了...我想要所有可能的匹配(就像我上面提到的那样)。 - Cypher
是的,只找到了一个匹配项,因为所有的字符都被上述模式匹配到了。没有其他待匹配的字符了。如果输入有两行像上面那样,你会得到两个匹配项。 - Avinash Raj
@Cypher 使用额外的捕获组在单个程序中获取上述结果。正则表达式不会循环遍历每个数字或字符。 - Avinash Raj

3
(.*?)(\\d+)(.*)

通过使用*?将您的*贪婪量词变为非贪婪量词。

因为您的第一个组(.*)是贪婪的,它会捕获所有内容,并只留下一个0\d捕获。如果您将其变为非贪婪的,它将给您预期的结果。请参见示例。

https://regex101.com/r/tX2bH4/53


谢谢... 但这只返回了一个匹配项... 如果我想让它返回所有的匹配项怎么办? - Cypher
@Cypher,你可以通过获取m.group(1)m.group(2)等来访问所有内容。请参见演示。 - vks
@Cypher,你说的所有匹配是什么意思? - Avinash Raj
你觉得类似于“这是针对QT30 / 00 / ! OK放置的?”这样的回答是否可接受?(或者我错了?) - Cypher
@Cypher 请尝试使用 (.*?\d{2})(\d+)(.*) - vks

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