正则表达式分裂成重叠的字符串

我正在探索正则表达式的强大功能,所以我只是想知道这样的事情是否可行:
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]"
    }
}
如果可能的话,那么只需提供正则表达式(并预先解释它是如何工作的)。 如果它只能用于Java之外的一些正则表达式,那么也可以随意提供它们。 如果不可能,那么请解释原因。 奖金问题 同样的问题,但是使用
find()
循环而不是
split
    Matcher m = Pattern.compile(BONUS_REGEX).matcher("12345");
    while (m.find()) {
        System.out.println(m.group());
    } // prints "12", "23", "34", "45"
请注意,并不是因为我有一个具体的任务以某种方式完成,而是我想要理解正则表达式。我不需要做我想要的代码;我想要正则表达式,如果它们存在,我可以在上面的代码中使用它来完成任务(或者其他版本的正则表达式使用代码“直接翻译”成另一种语言)。 如果它们不存在,我想要一个很好的解释原因。     
已邀请:
我觉得这不可能用
split()
,但用
find()
这很简单。只需使用内置捕获组的前瞻:
Matcher m = Pattern.compile("(?=(\d\d)).").matcher("12345");
while (m.find())
{
  System.out.println(m.group(1));
}
许多人没有意识到在比赛之后可以像任何其他捕获一样引用在前瞻或后看中捕获的文本。在这种情况下,它特别违反直觉,其中捕获是“整体”匹配的超集。 事实上,即使正则表达式整体上没有任何匹配也是有效的。从上面的正则表达式(
"(?=(\d\d))"
)中删除点,你会得到相同的结果。这是因为,只要成功匹配不消耗任何字符,正则表达式引擎会在尝试再次匹配之前自动向前碰撞一个位置,以防止无限循环。 然而,这种技术没有
split()
等价物,至少在Java中没有。虽然您可以拆分外观和其他零宽度断言,但是无法使相同的字符出现在多个生成的子字符串中。     
使用
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]" 
    } 
} 
EDIT1
match()
:为什么到目前为止没有人能像你的
BONUS_REGEX
那样编写一个正则表达式的原因在于
Matcher
,这将继续寻找前一组结束的下一组(即没有重叠),因为选择了之前的组之后开始 - 也就是说,没有明确地重新指定开始搜索位置(上图)。
BONUS_REGEX
的一个很好的候选人应该是
"(.\G.|^..)"
但是,不幸的是,
G
-anchor-in-the-middle技巧不适用于Java的
Match
(但在Perl中运行得很好):
 perl -e 'while ("12345"=~/(^..|.G.)/g) { print "$1n" }'
 12
 23
 34
 45
split()
:至于
INSERT_REGEX_HERE
一个好的候选人应该是
(?<=..)(?=..)
(分割点是零宽度位置,我右边有两个字符,左边有两个字符),但是再次,因为
split
没有重叠,你最终得到了
[12, 3, 45]
(虽然很近,但没有雪茄。) EDIT2 为了好玩,你可以通过先加倍非边界字符来欺骗
split()
做你想要的东西(这里你需要一个保留的字符值来分开):
Pattern.compile("((?<=.).(?=.))").matcher("12345").replaceAll("$1#$1").split("#")
我们可以聪明并且通过利用零宽度前瞻断言(与后视不同)可以具有无限长度的事实来消除对保留字符的需要;因此,我们可以分开所有点,这些点是双倍字符串末尾的偶数个字符(并且距离它的开头至少两个字符),产生与上面相同的结果:
Pattern.compile("((?<=.).(?=.))").matcher("12345").replaceAll("$1$1").split("(?<=..)(?=(..)*$)")
或者以类似的方式欺骗​​
match()
(但不需要保留的字符值):
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" 
    

bab

拆分将一个字符串切成多个部分,但这不允许重叠。你需要使用一个循环来获得重叠的部分。     
我不认为你可以使用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-and-replace表达式表示:匹配前两个相邻的数字,并用两个数字中的第二个替换字符串中的数字。     
替代方案,使用与Perl的普通匹配。应该在前瞻性的任何地方工作。这里不需要循环。
 $_ = '12345';
 @list = /(?=(..))./g;
 print "@list";

 # Output:
 # 12 23 34 45
但是,正如之前发布的那样,如果 G技巧有效,这个更好:
 $_ = '12345';
 @list = /^..|.G./g;
 print "@list";

 # Output:
 # 12 23 34 45
编辑:对不起,没看到所有这些都已经发布了。     

要回复问题请先登录注册