在Java中拆分字符串:具有可变长度的lookahead和lookbehind

时间:2015-07-21 12:14:11

标签: java regex string

我想使用数字作为分隔符来破坏Java中的String,但保留数字。一些研究表明,使用String中的split method()是合适的,但我不明白该怎么做。为了进一步解释我的问题,我将使用一个例子:

Input: 20.55|50|0.5|20|20.55

Required Output: ["20.55","|","50","|","0.5","|","20","|","20.55"]

通过调用方法拆分,就像我在下面给出的示例一样,没有lookahead和lookbehind,我得到了我期待的输出

expression.split("([0-9]+(\\.[0-9]+)?)")

Output: ["|","|","|","|"]

但如果我试着用前瞻来做这件事:

expression.split("(?=([0-9]+(\\.[0-9]+)?))")

Output: ["2","0.","5","5|","5","0|","0.","5|","2","0|","2","0.","5","5"]    

通过使用lookbehind我得到一个例外:

  

线程“main”中的异常java.util.regex.PatternSyntaxException:   Look-behind组在索引附近没有明显的最大长度   22(?< =([0-9] +(。[0-9] +)?))

任何人都可以向我解释这种行为,并建议一个解决方案吗?

PS:我知道我可以使用'|'打破字符串,但这只是一个愚蠢的例子,我实际上需要一个更复杂的正则表达式...

修改

由于分隔符的长度,似乎无法做我想要的事情。由于我正在寻找一个解决较小问题的解决方案,然后我可以将其用于剩下的练习,我将重新说明是否有转变,就像在第二和第三个答案中找到的那样:

我想打破包含算术表达式的Java中的String,并保留其所有项目。例如:

Input: 20.55 * 0.5 ** cos(360) + sin 0 * cos 90 + 1 * sin (180 + 90) * 0
Output: ["20.55", "*", "0.5", "**", "cos", "(", "360", ")", "+", "sin", "0", "*", "cos", "90", "+", "1", "*", "sin", "(", "180", "+", "90", ")", "*", "0"] 

PSS:请注意我必须使用'**'进行取幂。

编辑2 根据anubhava给出的答案,发现一个解决方案打破了所有项目的算术表达式

Pattern p = Pattern.compile( "\\*\\*|sin|cos|tan|\\d+(?:\\.\\d+)?|[-()+*/%]" );
Matcher matcher = p.matcher(expression);

while(matcher.find())
    System.out.println(matcher.group());

3 个答案:

答案 0 :(得分:2)

您可以使用此基于外观的正则表达式进行拆分:

String[] toks = "20.55|50|0.5|20|20.55".split( "(?=[^\\d.])|(?<=[^\\d.])" );

for (String tok: toks)
    System.out.printf("%s%n", tok);

RegEx Demo

<强>更新

您可以将此正则表达式用于匹配您的令牌:

Pattern p = Pattern.compile( "sin|cos|tan|\\d+(?:\\.\d+)?|[-()+*/%]" );

然后,您可以在while循环中使用Matcher#find()方法来获取所有匹配的令牌。

答案 1 :(得分:1)

问题在于您无法定义具有可变长度的外观。 +*?都匹配可变数量的字符。这是大多数正则表达式引擎的限制。

但是,您可以使用可变长度的前瞻。但在你的情况下,这不会起作用,因为外观不会消耗已经匹配的数据。

你想要的东西:

([0-9]+(\\.[0-9]+)?)\\K

\K所做的只是抛弃已经匹配的内容。因此,您仍将按某些位置进行拆分,并且不会使用浮动数字重复自己。

答案 2 :(得分:1)

尝试:

(?<=\d)(?=\|)|(?<=\|)(?=\d)

DEMO

在Java中:

public class RegexTest{
    public static void main(String[] args){
        String string = "20.55|50|0.5|20|20.55";
        System.out.println(Arrays.toString(string.split("(?<=\\d)(?=\\|)|(?<=\\|)(?=\\d)")));
    }
}

结果:

  

[20.55,|,50,|,0.5,|,20,|,20.55]

修改

使用其他字符作为分隔符来包含&#34; *&#34;,&#34; sin&#34;等等,您可以将正则表达式更改为:

(?<=[0-9a-z*])(?=\|)|(?<=\|)(?=[0-9a-z*])

DEMO

其中[0-9a-z*]表示数字,字母或&#34; *&#34;。如果要包含其他字符,只需将其添加到字符类,如[0-9a-z*E]等。