所有的重叠子匹配一个Java正则表达式(All overlapping substrings mat

2019-06-26 00:07发布

是否有返回匹配正则表达式的(可能重叠的)的子串的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)方法在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正则表达式(虽然它似乎没有在一段时间更新)。 具体而言, 在非打破搜索文件规定:

使用非中断搜索,你可以找到一个模式的所有可能的occureneces,包括那些交叉或嵌套。 这是通过使用匹配器的方法进行()代替find()方法实现

Answer 1:

我遇到类似的情况,我想上面的答案,但在我的情况下,它通过设置匹配的开始和结束索引占用了太多的时间,但我想我已经找到了一个更好的解决办法,我在这里张贴他人。 所以,下面是我的代码sniplet。

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()方法来检查,如果我已经达到文本的末尾。

希望这可以帮助。 谢谢!



Answer 2:

它是可行的O(N) 仅当指定允许的数量的长度的范围内

让我们从2-4的数字( 数字00-9999)说: (?=(\\d{2}))(?=(\\1\\d)?)(?=(\\2\\d)?)

这是通过正先行零长度断言,捕捉这种先行成组。 结果是可以在正则表达式内的输入与重复和空字符串(对于非匹配捕获)中找到,所有一起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代码的答案上面的脚本。



Answer 3:

你可以得到的最接近的是这样的。

"(?=((\\d*)\\d))(?=(\\d)\\d*)"

其结果将是在捕获组1,2和3。

至于我的想象可以走了,我只能认为在零长度断言捕获作为一种可行的方式来夺回字符串的相同位置。 捕捉零长度断言外文本会消耗文本一劳永逸(向后看只能捕获固定长度在Java中,因此它可以被认为是无法访问)。

该解决方案是不完美的:除了重复(!在同一位置文本)空字符串匹配和,也不会获得所有可能的子字符串。

捕获所有可能的子串的一种方法是构造如下的正则表达式与1N的起动值:

"(?=(\\d{" + n + "}))"

而对阵这个字符串递增的n值,直到没有匹配。

这个方法当然,低效相比所有数字用“\ d +”,并提取所有子串匹配的方法。



文章来源: All overlapping substrings matching a java regex