是否有一种 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)
in 中的方法更安全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)
}
}
}
我不确定最优雅的解决方案是什么。一种方法是在检查是否匹配line
之前获取一个子字符串并用适当的边界字符填充。pat
编辑 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 + ")");
}
}
}
}
编辑 4
这是一个更好的方法:https ://stackoverflow.com/a/11372670/244526
编辑 5
JRegex库支持查找与java 正则表达式匹配的所有重叠子字符串(尽管它似乎有一段时间没有更新)。具体来说,关于不间断搜索的文档指定:
使用不间断搜索,您可以找到模式的所有可能出现,包括那些相交或嵌套的模式。这是通过使用 Matcher 的方法 continue() 而不是 find() 来实现的