代码之家  ›  专栏  ›  技术社区  ›  polygenelubricants

正则表达式拆分为重叠字符串

  •  3
  • polygenelubricants  · 技术社区  · 15 年前

    我正在探索正则表达式的威力,所以我想知道这样的事情是否可能:

    public class StringSplit {
        public static void main(String args[]) {
            System.out.println(
                java.util.Arrays.deepToString(
                    "12345".split(INSERT_REGEX_HERE)
                )
            ); // prints "[12, 23, 34, 45]"
        }
    }
    

    如果可能的话,只需提供regex(并先发制人地解释它是如何工作的)。

    如果只在Java以外的一些正则表达式中使用,那么也可以免费提供。

    如果不可能,请解释原因。


    奖金问题

    同样的问题,但是 find() 循环而不是 split :

        Matcher m = Pattern.compile(BONUS_REGEX).matcher("12345");
        while (m.find()) {
            System.out.println(m.group());
        } // prints "12", "23", "34", "45"
    

    请注意,这并不是说我有一个具体的任务来完成一种或另一种方式,而是我想要理解正则表达式。我不需要做我想做的事情的代码;我需要正则表达式,如果它们存在的话,我可以在上面的代码中使用它们来完成任务(或者使用其他风格的正则表达式来将代码“直接翻译”成另一种语言)。

    如果它们不存在,我希望有一个很好的可靠的解释。

    5 回复  |  直到 13 年前
        1
  •  5
  •   Alan Moore Chris Ballance    15 年前

    我认为这是不可能的 split() ,但使用 find() 很简单。只需在捕获组内使用先行:

    Matcher m = Pattern.compile("(?=(\\d\\d)).").matcher("12345");
    while (m.find())
    {
      System.out.println(m.group(1));
    }
    

    许多人不知道在lookahead或lookback中捕获的文本可以在匹配后引用,就像其他捕获一样。在这种情况下,捕捉是“整体”匹配的一个超集,这是非常违反直觉的。

    事实上,即使regex作为一个整体不匹配,它也可以工作。从上面的正则表达式中删除点( "(?=(\\d\\d))" )你会得到同样的结果。这是因为,每当成功的匹配不消耗任何字符时,regex引擎会在尝试再次匹配之前自动向前移动一个位置,以防止出现无限循环。

    没有 拆分() 但是,至少在Java中,这种技术是等价的。尽管您可以对查找和其他零宽度断言进行拆分,但无法使同一个字符出现在多个子字符串中。

        2
  •  4
  •   Tim Cooper    14 年前

    这个有点重的实现使用 Matcher.find 而不是 split 也可以,不过到了你需要编码的时候 for 对于这样一个简单的任务,您还可以完全删除正则表达式并使用子字符串(对于类似的编码复杂性减去CPU周期):

    import java.util.*;
    import java.util.regex.*;
    
    public class StringSplit { 
        public static void main(String args[]) { 
            ArrayList<String> result = new ArrayList<String>();
            for (Matcher m = Pattern.compile("..").matcher("12345"); m.find(result.isEmpty() ? 0 : m.start() + 1); result.add(m.group()));
            System.out.println( result.toString() ); // prints "[12, 23, 34, 45]" 
        } 
    } 
    

    编辑1

    match() :为什么到目前为止还没有人能够编造出像 BONUS_REGEX 就在里面 Matcher ,它将继续查找上一个组结束的下一个组(即没有重叠),这取决于上一个组开始的位置之后——也就是说,没有明确地重新定义开始搜索位置(上面)。很好的候选人 奖金 本来应该是 "(.\\G.|^..)" 但不幸的是, \G 在中间技巧中的锚不适用于Java Match (但在Perl中工作得很好):

     perl -e 'while ("12345"=~/(^..|.\G.)/g) { print "$1\n" }'
     12
     23
     34
     45
    

    split() :至于 INSERT_REGEX_HERE 一个好的候选人 (?<=..)(?=..) (拆分点是零宽度的位置,在这里我有两个字符在右边,两个在左边),但同样,因为 分裂 减少你最后的重叠 [12, 3, 45] (很近,但没有雪茄。)

    编辑2

    为了好玩,你可以耍花招 拆分() 首先将非边界字符加倍(此处需要保留字符值来拆分),以完成所需操作:

    Pattern.compile("((?<=.).(?=.))").matcher("12345").replaceAll("$1#$1").split("#")
    

    我们可以利用零宽度这一事实,聪明地消除对保留字符的需要。 展望未来 断言(与后面的查看不同)可以有无限长;因此,我们可以围绕 偶数个字符之外 从双字符串的结尾(至少从其开头开始两个字符),产生与上面相同的结果:

    Pattern.compile("((?<=.).(?=.))").matcher("12345").replaceAll("$1$1").split("(?<=..)(?=(..)*$)")
    

    或者是欺骗 匹配() 以类似的方式(但不需要保留字符值):

    Matcher m = Pattern.compile("..").matcher(
      Pattern.compile("((?<=.).(?=.))").matcher("12345").replaceAll("$1$1")
    );
    while (m.find()) { 
        System.out.println(m.group()); 
    } // prints "12", "23", "34", "45" 
    
        3
  •  1
  •   Ry4an Brase    15 年前

    把一根弦劈成多段,但这不允许重叠。你需要用一个循环来得到重叠的部分。

        4
  •  1
  •   Ian C.    15 年前

    我认为用split()不能这样做,因为它会丢弃与正则表达式匹配的部分。

    在Perl中,这项工作:

    my $string = '12345';
    my @array = ();
    while ( $string =~ s/(\d(\d))/$2/ ) {
        push(@array, $1);
    }
    print join(" ", @array);
    # prints: 12 23 34 45
    

    find和replace表达式表示:匹配前两个相邻的数字,并用两个数字中的第二个数字替换字符串中的数字。

        5
  •  0
  •   Alan Moore Chris Ballance    15 年前

    或者,使用与Perl的纯匹配。应该在任何有风头的地方工作。这里不需要循环。

     $_ = '12345';
     @list = /(?=(..))./g;
     print "@list";
    
     # Output:
     # 12 23 34 45
    

    但如前所述,如果这个技巧奏效的话,这个就更好了:

     $_ = '12345';
     @list = /^..|.\G./g;
     print "@list";
    
     # Output:
     # 12 23 34 45
    

    编辑 :对不起,没看见 全部的 其中的已发布。