所有重叠的子字符串都匹配java正则表达式

dsg*_*dsg 5 java regex matcher

是否有API方法返回与正则表达式匹配的所有(可能重叠的)子串?

例如,我有一个文本字符串:String t = 04/31 412-555-1235;,我有一个模式:Pattern p = new Pattern("\\d\\d+");匹配两个或多个字符的字符串.

我得到的比赛是:04,31,412,555,1235.

如何获得重叠匹配?

我希望代码返回:04,31,41,412,12,55,555,55,12,123,1235,23,235,35.

理论上它应该是可能的 - 有一个明显的O(n^2)算法枚举和检查所有子串与模式.

编辑

而不是枚举所有子串,使用该region(int start, int end)方法更安全Matcher.根据单独的,提取的子字符串检查模式可能会更改匹配的结果(例如,如果在模式的开头/结尾处存在非捕获组或字边界检查).

编辑2

实际上,目前还不清楚region()你对零宽度匹配的期望是否正确.规范含糊不清,实验结果令人失望.

例如:

String line = "xx90xx";
String pat = "\\b90\\b";
System.out.println(Pattern.compile(pat).matcher(line).find()); // prints false
for (int i = 0; i < line.length(); ++i) {
  for (int j = i + 1; j <= line.length(); ++j) {
    Matcher m = Pattern.compile(pat).matcher(line).region(i, j);
    if (m.find() && m.group().size == (j - i)) {
      System.out.println(m.group() + " (" + i + ", " + j + ")"); // prints 90 (2, 4)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我不确定最优雅的解决方案是什么.一种方法是line在检查是否pat匹配之前采用带有适当边界字符的子字符串和pad .

编辑3

这是我提出的完整解决方案.它可以处理原始正则表达式中的零宽度模式,边界等.它查看文本字符串的所有子字符串,并通过在开头和结尾用适当数量的通配符填充模式来检查正则表达式是否仅在特定位置匹配.它似乎适用于我尝试的案例 - 虽然我没有做过广泛的测试.它肯定效率低于它可能的效率.

  public static void allMatches(String text, String regex)
  {
    for (int i = 0; i < text.length(); ++i) {
      for (int j = i + 1; j <= text.length(); ++j) {
        String positionSpecificPattern = "((?<=^.{"+i+"})("+regex+")(?=.{"+(text.length() - j)+"}$))";
        Matcher m = Pattern.compile(positionSpecificPattern).matcher(text);

        if (m.find()) 
        {   
          System.out.println("Match found: \"" + (m.group()) + "\" at position [" + i + ", " + j + ")");
        }   
      }   
    }   
  }
Run Code Online (Sandbox Code Playgroud)

编辑4

这是一个更好的方法:https://stackoverflow.com/a/11372670/244526

编辑5

JRegex库支持查找所有重叠的子匹配一个Java正则表达式(虽然它似乎没有在一段时间更新).具体而言,关于非破坏性搜索文档指定:

使用非中断搜索,您可以查找模式的所有可能发生的事件,包括相交或嵌套的模式.这是通过使用Matcher的方法proceed()而不是find()来实现的.

nha*_*tdh 0

你能得到的最接近的是这样的。

"(?=((\\d*)\\d))(?=(\\d)\\d*)"
Run Code Online (Sandbox Code Playgroud)

结果将是捕获组 1、2 和 3。

就我的想象力而言,我只能认为以零长度断言捕获是重新捕获字符串相同位置的可行方法。捕获零长度断言之外的文本将一次性消耗该文本(look-behind 在 Java 中只能捕获固定长度,因此可以认为是不可访问的)。

这个解决方案并不完美:除了重复(同一位置的文本!)和空字符串匹配之外,它不会捕获所有可能的子字符串。

捕获所有可能的子字符串的一种方法是构造以下正则表达式,其 n 值从 1 开始:

"(?=(\\d{" + n + "}))"
Run Code Online (Sandbox Code Playgroud)

并将该字符串与此匹配,以递增 n 的值,直到没有匹配项为止。

当然,与将所有数字与“\d+”匹配并提取所有子串的方法相比,这种方法效率较低。