例如,我有一个文本字符串:
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)算法,可以枚举并检查所有子字符串是否与模式匹配。
与其枚举所有子字符串,不如在Matcher中使用region(int start, int end)方法更安全。对单独提取的子字符串检查模式可能会改变匹配结果(例如,如果模式的开头/结尾有非捕获组或单词边界检查)。
实际上,对于零宽度匹配,
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)
}
}
}
我不确定最优雅的解决方案是什么。一种方法是在检查
pat
是否匹配之前,取line
的子字符串,并用适当的边界字符填充。这是我想出的完整解决方案。它可以处理原始正则表达式中的零宽模式、边界等。它遍历文本字符串的所有子字符串,并通过在模式的开头和结尾填充适当数量的通配符来检查正则表达式是否仅在特定位置匹配。根据我尝试的情况,它似乎可以工作 - 尽管我没有进行广泛的测试。它肯定比可能更高效。
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 + ")");
}
}
}
}
这是一个更好的做法:https://stackoverflow.com/a/11372670/244526
JRegex库支持查找与Java正则表达式匹配的所有重叠子字符串(尽管似乎有一段时间没有更新了)。具体来说,关于非断开搜索的文档指定:
使用非断开搜索,您可以找到模式的所有可能出现,包括相交或嵌套的出现。这是通过使用Matcher的proceed()方法而不是find()方法实现的。