如何使Java字符串分裂贪婪与前瞻?

时间:2016-03-10 11:04:47

标签: java regex split

代码基本上是:

T, &, &, T, &, T

我期待前瞻是贪婪的,但它正在返回数组:

T, &&, T, &, T

我的目标是:

"(?=\\w|&&?)"

分裂和前瞻是否可行?

我尝试了以下拆分正则表达式值,但结果仍然不是吝啬的&符号:

"(?=\\w|&{1,2})"

public class DateConverter{ public static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); public static Date convertStringToDate(final String str){ try{ return DATE_FORMAT.parse(str); } catch(Exception ex){ //TODO: Log exception return null; } } public static String convertDateToString(final Date date){ try{ return DATE_FORMAT.format(date); } catch(Exception ex){ //TODO: Log exception return null; } } }

4 个答案:

答案 0 :(得分:2)

它已经贪婪了,但我认为你误解了你的分裂是如何运作的。问题是你在考虑角色而不是它们之间的空间(这是正则表达式可以远离你的地方之一)。

您要求分割字符串中的下一个字符是单词字符或一系列&符号的位置。在你的字符串中,让我们标记满足该字符串的地方:

T|&|&|T|&|T

在第一个T和第一个&符号之间的空格中,下一个字符是&符号(匹配(?=&),它在你的正则表达式中有效),两个&符号之间的空格也匹配同样的原因。 &符号和第二个T之间的空格也匹配(匹配(?=\w)),依此类推。

split函数将测试字符串中的每个空格,以确定它是否是分割位置的候选者。要做你想做的事,你必须小心使用前瞻,这样我们就不允许在一串&符号的中间分裂。

有多种方法可以克服这个问题; WiktorStribiżew提供了一条适用于他的评论的建议。

通常使用后视来检查您是否重复不需要的角色会起作用,或者如果可能的话,您可以使用后视来识别匹配的位置,并使用前瞻来避免不希望的重复。例如,如果我们希望将所有字符分开,将重复的字符放在一起,则可以(?<=(.))(?!\\1)将您的示例拆分为T, &&, T, &, T

答案 1 :(得分:1)

这个怎么样:

"(?=\\w)|(?<=\\w)"

或允许重复T:

"(?<!\\w)(?=\\w)|(?<=\\w)(?!\\w)"

或最佳形式here

答案 2 :(得分:1)

Lookarounds不能贪婪或不情愿,他们只是检查左侧(lookbehind)和右侧(lookahead)的相邻文本是否与环视子模式匹配。如果匹配,并且环视为正,则匹配空位置。如果没有锚定环视,则字符串中的每个位置都将针对环视中的模式进行测试,甚至是开头和结尾。请参阅此截图(显示您的(?=\w|&&?)):

enter image description here

由于环视是零宽度断言并且它不消耗字符,因此测试所有位置(在每个字符之前和结尾处)。因此,您可以获得每个角色之间的匹配。

(?=\w|&&?)检查T之前的第一个位置:它与\w匹配,因此该位置匹配(请参阅第一个|)。然后是T之前的第一个&之后的下一个位置。它符合&&。然后,正则表达式引擎继续检查第一个&和第二个&之后的位置。它匹配,因为后面有一个&。这样,我们匹配到最后。结束位置不匹配,因为未跟&或单词字符。

您可以使用另一种环视来限制环视中的模式,以避免在输入字符串中匹配特定的位置

(?=\w|(?<!&)&)
      ^^^^^^

(?<!&)&模式将与之前没有其他&的{​​{1}}匹配。请参阅regex demo

IDEONE demo

&

环视解决方案是通用解决方案。如果我们要考虑当前的情况,你肯定可以缩短&#34;模式为String[] result = "T&&T&T".split("(?=\\w|(?<!&)&)"); System.out.println(Arrays.toString(result)); // => [T, &&, T, &, T] (也将find a match at the end of the string,尽管Java String#split将安全地从结果数组中删除尾随的空元素),该模式匹配非单词和单词字符之间的所有位置,也如果字符串的开头/结尾有一个单词字符,则在字符串的开头/结尾处。如果备选方案(例如正则表达式中的\b\w)属于同一类型(例如,两个都是单词字符),则无法使用。

答案 3 :(得分:0)

看起来你想在不同的字符之间进行分割,所以一般来说:

String[] parts = input.split("(?<=T)(?=&)|(?<=&)(?=T)");

但在这种情况下,除了开头/结尾外,您可以拆分字边界:

String[] parts = input.split("(?<=.)\b(?=.)");