有没有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)
算法,它根据模式枚举并检查所有子字符串。
编辑
使用
匹配器中的
区域(int start,int end)
方法比枚举所有子字符串更安全。根据单独提取的子字符串检查模式可能会更改匹配结果(例如,如果在模式的开始/结束处存在非捕获组或单词边界检查)。
编辑2
实际上,还不清楚
区域()
是否满足零宽度匹配的要求。规范是模糊的,实验结果令人失望。
例如:
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正则表达式匹配的所有重叠子字符串(尽管它似乎暂时没有更新)。具体而言,关于非破坏性搜索的文档规定:
使用非中断搜索,您可以找到一个模式的所有可能发生的情况,包括那些相交或嵌套的情况。这是通过使用Matcher的方法procedure()而不是find()实现的
你能得到的最接近的东西是这样的。
"(?=((\\d*)\\d))(?=(\\d)\\d*)"
结果将捕获第1组、第2组和第3组。
就我的想象而言,我只能认为在零长度断言中捕获是重新获取字符串相同位置的可行方法。捕获零长度断言之外的文本将一次性使用文本(look behind在Java中只能捕获固定长度的文本,因此可以认为它不可访问)。
这个解决方案并不完美:除了重复(相同位置的文本!)和空字符串匹配,它不会捕获所有可能的子字符串。
捕获所有可能的子字符串的一种方法是构造以下正则表达式,其中n的值从1开始:
"(?=(\\d{" + n + "}))"
并将字符串与之匹配,以增加n的值,直到没有匹配为止。
当然,与用“\d”匹配所有数字并提取所有子串的方法相比,这种方法效率低下。
只有在指定允许的数字长度范围时,它才可以作为O(n)。
假设从2-4位数字(数字00-9999):(?=(\\d{2}))(?=(\\1\\d)?)(?=(\\2\\d)?)
这是一个通过正向前瞻的零长度断言,将此类前瞻捕获到组中。结果是在regex输入中可以找到的所有2-4位字符串的数组,以及重复字符串和空字符串(用于非匹配捕获)。
我不是Java开发人员,但我相信Perl脚本也可以作为示例阅读。
#!/usr/bin/perl # perl script
use List::MoreUtils qw/ uniq /; # uniq subroutine library
$_ = '04/31 412-555-1235'; # input
my @n = uniq (/(?=(\d{2}))(?=(\1\d)?)(?=(\2\d)?)/g); # regex (single slash in Perl)
print "$_\n" for grep(/\S/, @n); # print non-empty lines
诀窍是使用反向引用。如果您想捕获2-5位字符串,则需要在正则表达式中使用一个更积极的前瞻:
(?=(\\d{2}))(?=(\\1\\d)?)(?=(\\2\\d)?)(?=(\\3\\d)?)
。
我相信这是你能采取的最接近的方法。如果这对你有用,请发表评论,希望一些Java开发人员能用上面脚本的Java代码编辑我的答案。
我遇到了类似的情况,我尝试了上述答案,但在我的情况下,设置匹配器的开始和结束索引花费了太多时间,但我认为我找到了一个更好的解决方案,我在这里发布给其他人。下面是我的代码剪。
if (textToParse != null) {
Matcher matcher = PLACEHOLDER_PATTERN.matcher(textToParse);
while(matcher.hitEnd()!=true){
Boolean result = matcher.find();
int count = matcher.groupCount();
System.out.println("Result " +result+" count "+count);
if(result==true && count==1){
mergeFieldName = matcher.group(1);
mergeFieldNames.add(mergeFieldName);
}
}
}
我用过火柴。方法来检查我是否已到达文本的末尾。
希望这有帮助。谢谢
问题内容: 在下面的代码中: 结果是: 我希望成为 如何实现? 问题答案: 使匹配器尝试从后者开始下一个扫描。
问题内容: 我试图创建下面的正则表达式:返回之间的字符串和(或或)从下列字符串RNA: ,让所有的比赛会被发现,包括重叠的。 我尝试了几种正则表达式,最后得到了类似的结果: 您能告诉我我的正则表达式模式中的错误吗? 问题答案: 用一个正则表达式执行此操作实际上是非常困难的,因为大多数用法 都不 希望重叠匹配。但是,您可以通过一些简单的迭代来做到这一点: 虽然,这有一些问题。您希望得到的回报是什么?
问题内容: 我正在尝试在Python 2.6中使用re查找更大系列的数字中的每10位数字系列。 我很容易就能抓住不重叠的比赛,但我希望数字系列中的每场比赛。例如。 在中 我应该得到以下列表: 我已经找到了“超前”的引用,但是我所看到的示例仅显示数字对,而不是较大的分组,而且我无法将其转换为两位数以外的数字。 问题答案: 在前瞻范围内使用捕获组。前瞻捕捉你感兴趣的文本,但是实际匹配在技术上是前瞻之前
这是与java正则表达式匹配的所有重叠子字符串的后续。 有没有办法让这段代码更快?
我试图使用Python2.6中的re在一个更大的数字系列中找到每10位数字系列。 我很容易抓住没有重叠的比赛,但我想要的每一个比赛在数字系列。例如。 在“123456789123456789” 我应该得到以下列表: 我发现了对“前瞻”的引用,但我看到的示例只显示了数字对,而不是更大的分组,并且我无法将它们转换成两位数以外的数字。
我试图匹配所有包含引号的句子,独立于引号的长度或引号内的句子数量。 正如Alfe所指出的,得到一个完美的正则表达式可能是不可行的,但是如果可能的话,我想改进我正在使用的正则表达式。 现在我这样做是为了找到报价: 但是我想确保引用出现在一个句子中,然后捕捉整个句子。 我所说的一句话是指一段文字: 通常前面有空格 正如阿尔夫指出的那样,这并不能涵盖所有的句子,但如果我能满足这些条件,那就足够了。 例如