【发布时间】:2012-07-03 10:28:04
【问题描述】:
是否有返回所有(可能重叠)匹配正则表达式的子字符串的 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) 算法可以枚举并检查所有子字符串是否符合模式。
编辑
与其枚举所有子字符串,不如在Matcher 中使用region(int start, int end) 方法更安全。根据单独的提取子字符串检查模式可能会更改匹配结果(例如,如果在模式的开始/结束处存在非捕获组或单词边界检查)。
编辑 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)
}
}
}
我不确定最优雅的解决方案是什么。一种方法是在检查pat 是否匹配之前获取line 的子字符串并用适当的边界字符填充。
编辑 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 正则表达式匹配的所有重叠子字符串(尽管它似乎有一段时间没有更新)。具体来说,documentation on non-breaking search 指定:
使用不间断搜索,您可以找到所有可能出现的 模式,包括那些相交或嵌套的模式。这是 通过使用 Matcher 的方法proceed() 而不是 find() 来实现
【问题讨论】:
-
只需对所有 3 个或更多字符结果进行正则表达式后循环
-
regexlib.com 可能是一个进行挖掘的好地方。
-
@Ωmega 尽我所能,但欢迎无用的反馈。干杯。
-
我认为正则表达式不会进行重复扫描。大海捞针中的角色只能使用一次。我能想到的最接近的方法是不贪婪的匹配。但这只会返回 12、35 而不会返回 1235。