Java模式匹配最大可能的组

时间:2015-07-03 16:47:38

标签: java regex regex-greedy

我有一个模式可以在字符串中找到重复的模式:

String patt = "(.+?)\\1+";

不幸的是这匹配" 003003003" as" 0"。我需要修改什么来使这个匹配成为" 003"?

3 个答案:

答案 0 :(得分:3)

只需删除问号,即使你的正则表达式贪婪。

Pattern patt = Pattern.compile("(.+)\\1+");

DEMO

为什么在打印组索引1后,正则表达式仅返回0300

因为.+?强制正则表达式引擎非贪婪地匹配任何字符一次或多次。因此,这与第一个0匹配,并检查是否存在另一个或多个零或后续。是的,它就在那里。因此它捕获第一个零并匹配第二个零。

现在需要第三个字符并进行检查。

使用锚点。

^(.+?)\\1+$

DEMO

答案 1 :(得分:0)

来自OP评论:But in case I have "000000" I would want "0"

已修改

如果可能,将首先匹配不同字符的重复组

事实证明(我相信)找到第一个不同的字符,
导致最大的不同组重复匹配。

这使得能够使用能够实现这一目的的非贪婪量词 用于大字符串而不会影响性能。

解释结果:
(注意 - 如果使用支持分支复位的引擎,则所有结果都可以是 从同一组获得)

组0包含整个匹配,然后;

要么:

  • 第1组
  • 中不同字符的重复组(最长
  • 第3组
  • 中相同()字符的重复组

正则表达式:((.)+?(?!\2).+?)\1+|(.)\3+

扩展:

   (                # (1 start), Different chars (at least 2) repeating group
        ( . )+?          # (2), Shortest distance sample character
        (?! \2 )         # Border between different characters
        .+?              # First different, shortest distance
   )                # (1 end)
   \1+ 
|                 # or
   ( . )            # (3), Same character repeating group
   \3+ 

输入:

000010910910901012222

输出:

 **  Grp 0 -  ( pos 0 , len 4 ) 
0000  
 **  Grp 1 -  NULL 
 **  Grp 2 -  NULL 
 **  Grp 3 -  ( pos 0 , len 1 ) 
0  
-------------
 **  Grp 0 -  ( pos 4 , len 9 ) 
109109109  
 **  Grp 1 -  ( pos 4 , len 3 ) 
109  
 **  Grp 2 -  ( pos 5 , len 1 ) 
0  
 **  Grp 3 -  NULL 
-------------
 **  Grp 0 -  ( pos 13 , len 4 ) 
0101  
 **  Grp 1 -  ( pos 13 , len 2 ) 
01  
 **  Grp 2 -  ( pos 13 , len 1 ) 
0  
 **  Grp 3 -  NULL 
-------------
 **  Grp 0 -  ( pos 17 , len 4 ) 
2222  
 **  Grp 1 -  NULL 
 **  Grp 2 -  NULL 
 **  Grp 3 -  ( pos 17 , len 1 ) 
2  

使用分支重置:组0 =完全匹配,组1 =重复组。

 # (?|((.)+?(?!\2).+?)\1+|(.)\1+)

 (?|
      (                # (1 start), Different chars (at least 2) repeating group
           ( . )+?          # (2), Shortest distance sample character
           (?! \2 )         # Border between different characters
           .+?              # First different, shortest distance
      )                # (1 end)
      \1+ 
   |                 # or
      ( . )            # (1), Same character repeating group
      \1+ 
 )

使用单一前瞻进行重叠匹配 组1 =完全匹配,组2 =不同的char重复组,重复组4单个字符。

 # (?=(((.)+?(?!\3).+?)\2+|(.)\4+))

 (?=
      (                # (1 start), Capture entire match
           (                # (2 start), Different chars (at least 2) repeating group
                ( . )+?          # (3), Shortest distance sample character
                (?! \3 )         # Border between different characters
                .+?              # First different, shortest distance
           )                # (2 end)
           \2+ 
        |                 # or
           ( . )            # (4), Same character repeating group
           \4+ 
      )                # (1 end)
 )

使用分支Rreset 和单一前瞻进行重叠匹配 第1组=整场比赛,第2组=重复组。

 # (?=((?|((.)+?(?!\3).+?)\2+|(.)\2+)))

 (?=
      (                # (1 start), Capture entire match
           (?|
                (                # (2 start), Different chars (at least 2) repeating group
                     ( . )+?          # (3), Shortest distance sample character
                     (?! \3 )         # Border between different characters
                     .+?              # First different, shortest distance
                )                # (2 end)
                \2+ 
             |                 # or
                ( . )            # (2), Same character repeating group
                \2+ 
           )
      )                # (1 end)
 )

答案 2 :(得分:0)

A" theorical"正则表达式可以是:

(?=(?<sub>(.+)(\\2+)))(?=(?<pattern>.+?)(?:\\4+\\3|\\2))

(在DOTALL模式下)

此正则表达式将为字符串中的每个位置找到最大的重复子字符串。

我说&#34;理论&#34;因为在实践中你不能在长字符串中使用它,因为解析整个字符串所需的步骤数量会随着字符串的大小而快速增长。因此,一个很好的折衷方案是限制搜索的子字符串的大小,例如:

(?=(?<sub>(.{1,20})(\\2+)))(?=(?<pattern>.{1,10}?)(?:\\4+\\3|\\2))

模式说明:

正则表达式分为两个前瞻断言,用于测试相同位置的字符串(因为外观是一个零宽度断言)。

第一个前瞻尝试找到一个子串(称为&#34; sub&#34;),它是具有最大模式的重复序列(组2)。这就是使用贪婪量词的原因。请注意,子串(\\2+)的结尾(在第一个模式之后)在第3组中捕获,因为第二个前瞻将需要它。

第二个先行的目标是检查同一子串的最短模式是什么。这次,使用非贪婪量词,并且当在新的较小图案的一次或多次重复之后到达组3时,或者当达到最大图案时(在这种情况下,没有较小的图案),前瞻成功。

模式细节:

(?= # first lookahead
    (?<sub>    # group "sub" (or group 1) with the whole substring
        (.+)   # group 2 with the largest pattern
        (\\2+) # group 3 with the end of the substring
    )
)
(?= # second lookahead
    (?<pattern>.+?) # the shortest pattern (group 4) ...
    (?:             # non-capturing group: ... must be followed by
        \\4+\\3     # one or more repetitions until the group 3
      |             # OR
        \\2         # until the largest pattern (group 2)
    )
)

演示:

import java.util.regex.*;

class repeatedSubstrings
{
    public static void main (String[] args) 
    {
        String s = "0070070071071071" + "\n"
                 + "Now is the winter of our discontent" + "\n"
                 + "Made glorious summer by this sun of York;" + "\n"
                 + "And all the clouds that lour'd upon our house" + "\n"
                 + "In the deep bosom of the ocean buried." + "\n"
                 + "Now are our brows bound with victorious wreaths;" + "\n"
                 + "Our bruised arms hung up for monuments;" + "\n"
                 + "Our stern alarums changed to merry meetings," + "\n"
                 + "Our dreadful marches to delightful measures." + "\n"
                 + "00000000" + "\n"
                 + "42424242";

        Pattern Reg = Pattern.compile("(?=(?<sub>(.+)(\\2+)))(?=(?<pattern>.+?)(?:\\4+\\3|\\2))", Pattern.DOTALL);

        Matcher m = Reg.matcher(s);

        int prevEnd = 0;
        int currEnd;

        long start = System.currentTimeMillis();

        StringBuilder display = new StringBuilder("substring      pattern   \tstart offset\tend offset\tlength\n");
        display.append("--------------------------------------------------------------------------------\n");

        while (m.find()) {
            currEnd = m.start() + m.group("sub").length();

            if (currEnd > prevEnd) {
                display.append(String.format("%-15s%-10s\t%-15d\t%-15d\t%d\n", m.group("sub"), m.group("pattern"), m.start(), currEnd, m.group("sub").length()));
                prevEnd = currEnd;
            }
        }

        long end = System.currentTimeMillis();

        System.out.println(display.toString());
        System.out.printf("Elapsed Time: %.1es\n", (end-start)/1000.0);
    }
}

注意:此正则表达式返回最大的子字符串,其中包含字符串中每个位置的重复模式。因此也返回重叠的子串。使用字符串0070070071071071,子字符串为007007007071071071。如果您不想要这种行为,只需在正则表达式的末尾添加\\1即可获得007007007107107

示例代码排除在前一个结果之后开始并在字符串中的相同偏移之前或之后结束的结果。如果您想获得所有这些结果,请删除if声明。