与java正则表达式匹配的所有重叠子字符串
是否有API方法返回与正则表达式匹配的所有(可能重叠)子字符串 例如,我有一个文本字符串:与java正则表达式匹配的所有重叠子字符串,java,regex,matcher,Java,Regex,Matcher,是否有API方法返回与正则表达式匹配的所有(可能重叠)子字符串 例如,我有一个文本字符串:string t=04/31412-555-1235,我有一个模式:模式p=new模式(\\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中的区域
string t=04/31412-555-1235代码>,我有一个模式:模式p=new模式(\\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
中的区域(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)
}
}
}
编辑4
这里有一个更好的方法:
编辑5
该库支持查找与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);
}
}
}
我已经使用matcher.hitEnd()方法来检查是否已到达文本的末尾
希望这有帮助。
谢谢 只要做一个post-regex循环,通过所有3个或更多的字符,结果可能是进行挖掘的好地方@Ωmega尽了我最大的努力,但对无用的反馈持开放态度。干杯。我想正则表达式不会重复扫描。干草堆中的角色只能使用一次。我能想到的最接近的方法是一场不激烈的比赛。但这只会返回12,35,不会得到1235。重复:正则表达式在Java中是相同的(除了反斜杠需要转义)。至于uniq
,它可以用Java中的Set
(TreeSet
或HashSet
)进行模拟。@nhahtdh-谢谢。请随意添加更新到我的答案编辑后。
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);
}
}
}