\ G如何在.split中工作?

时间:2018-05-16 07:57:08

标签: java regex string split match

我喜欢用Java进行代码打高尔夫(即使Java方式过于冗长而无法竞争),它正在以尽可能少的字节完成一定的挑战。在我的一个答案中,我有以下代码:

for(var p:"A4;B8;CU;EM;EW;E3;G6;G9;I1;L7;NZ;O0;R2;S5".split(";"))

在我们将其转换为带有.split的字符串数组后,它基本上遍历2-char字符串。有人建议我可以把它打到这个而不是节省4个字节:

for(var p:"A4B8CUEMEWE3G6G9I1L7NZO0R2S5".split("(?<=\\G..)"))

功能仍然相同。它循环遍历2-char Strings。

但是,我们都没有100%确定这是如何运作的,因此这个问题。

我所知道的:

我知道.split("(?<= ... )")用于分割,但保留尾随分隔符 还有一种方法可以将前导分隔符或分隔符保留为分隔项:

"a;b;c;d".split("(?<=;)")            // Results in ["a;", "b;", "c;", "d"]
"a;b;c;d".split("(?=;)")             // Results in ["a", ";b", ";c", ";d"]
"a;b;c;d".split("((?<=;)|(?=;))")    // Results in ["a", ";", "b", ";", "c", ";", "d"]

我知道\G用于在遇到不匹配后停止。
编辑: \G用于指示最后一个匹配结束的位置(或第一次运行的字符串的开头)。通过 @SebastianProske

int count = 0;
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("match,");
java.util.regex.Matcher matcher = pattern.matcher("match,match,match,blabla,match,match,");
while(matcher.find())
  count++;
System.out.println(count); // Results in 5

count = 0;
pattern = java.util.regex.Pattern.compile("\\Gmatch,");
matcher = pattern.matcher("match,match,match,blabla,match,match,");
while(matcher.find())
  count++;
System.out.println(count); // Results in 3

但是,在分割中使用.split("(?<=\\G..)")时,\G如何正常工作? 为什么.split("(?=\\G..)")不起作用?

Here a "Try it online"-link for all code-snippets described above to see them in action.

2 个答案:

答案 0 :(得分:9)

  

.split("(?<=\\G..)")如何运作

(?<=X)是X的零宽度正向后视。\G是上一个匹配的结束(不是某种停止指令)或输入的开始,当然{{1}是两个单独的字符。所以..是前一场比赛结束时的零宽度后视加两个字符。由于这是(?<=\G..)并且我们正在描述分隔符,因此使整个事物成为零宽度断言意味着我们只使用它来识别断开字符串的位置,而不是实际消耗任何字符串字符。

让我们一起走过split

  1. ABCDEF匹配输入的开头,\G匹配..,因此AB找到(?<=\G..)AB之间的零宽度空格因为这是一个后视:也就是说,CD 之前到正则表达式游标的第一个点是\G..AB之间的点。因此,在CDAB
  2. 之间分配
  3. CD标记了\G之后的位置,因此AB找到了(?<=\G..)CD之间的零宽度空间,因为正则表达式正向前移,这是EF匹配的第一个地方:\G..匹配\GABCD匹配..之间的位置。因此,在CDCD
  4. 之间分配
  5. 同样:EF标记\G之后的位置,以便CD找到(?<=\G..)和输入结束之间的零宽度空间。因此,在EF和结束输入之间进行分配。
  6. 创建一个包含所有匹配项的数组,除了末尾的空匹配项(因为这是EF,隐含split,最后丢弃空字符串。)
  7. 结果length = 0

      

    为什么{ "AB", "CD", "EF" }不起作用?

    因为.split("(?=\\G..)")是正面的提前。前一个匹配的结尾永远不会是正则表达式游标的提前。它只能后面。

答案 1 :(得分:5)

首先,\G定义:它是一个匹配字符串开头或上一个匹配结束的锚点。这是一个职位。它既不消耗字符也不改变光标位置。艾伦·摩尔之前在an answer中写道,{look}的内部\G行为是引擎特定的。这将在Java中以所需长度分割,但在PCRE中不会产生相同的结果。

那么(?<=\G..)中的\G如何运作?请看下面逐步演示dot和 ↓A4 \G..↓B8 \G..↓CU \G.. . . 匹配的位置:

\G

A匹配输入字符串的开头,然后点按顺序匹配48。引擎继续遍历并在CA 4 B 8 \G . . (?<=\G..) 之间停止。这里是后面的比赛:

\G

4匹配的位置是前一个点匹配的位置,即B之后和.之前的位置。此过程将继续到输入字符串的末尾。它将一个字符串拆分为2个单位的数据(这里安全地为一个字符)。它不适用于多行输入字符串,如果是这样,它会部分拆分,因为点\G与换行符不匹配,或者它根本不会拆分,因为.split("(?=\\G..)")不匹配一行的开头(只是输入字符串的开头)。

  

为什么{{1}}不起作用?

由于前瞻性 - 期待 - 它无法满足前一场比赛结束的地方。它只是继续走路,直到最后。