我正在学习regexp,并认为我开始抓紧了。但是之后...
我试图拆分一个字符串,我需要帮助来理解这样一个简单的事情:
String input = "abcde";
System.out.println("[a-z] " + Arrays.toString(input.split("[a-z]")));
System.out.println("\\w " + Arrays.toString(input.split("\\w")));
System.out.println("\\w*? " + Arrays.toString(input.split("\\w*?")));
System.out.println("\\w+? " + Arrays.toString(input.split("\\w+?")));
The output is
[a-z] - []
\w - []
\w*? - [, a, b, c, d, e]
\w+? - []
为什么两个第一行中的任何一行都没有在任何字符上拆分字符串?第三个表达式\ w *?,(问号防止贪婪)按照我的预期工作,在每个字符上分割字符串。星号,零个或多个匹配项返回一个空数组。
我在NotePad ++和程序中尝试了表达式,它显示了5个匹配项,如:
Scanner ls = new Scanner(input);
while(ls.hasNext())
System.out.format("%s ", ls.findInLine("\\w");
Output is: a b c d e
这真让我困惑
如果使用正则表达式拆分字符串,则基本上可以告诉应该剪切字符串的位置。这必然会削减你与正则表达式相匹配的东西。这意味着如果你在\w
分割,那么每个字符都是一个分裂点,它们之间的子串(全部为空)都会被返回。 Java会自动删除尾随的空字符串,如the documentation中所述。
这也解释了为什么懒惰匹配\w*?
会给你每个角色,因为它将匹配任何角色(零宽度)之间(和之前和之后)的每个位置。剩下的是字符串本身的字符。
让我们分解一下:
[a-z]
,\w
,\w+?
你的字符串是
abcde
比赛如下:
a b c d e
└─┘└─┘└─┘└─┘└─┘
这将留下匹配之间的子串,所有这些都是空的。
上述三个正则表达式在这方面表现相同,因为它们只匹配单个字符。 \w+?
会这样做,因为它没有任何其他限制可能使+?
尝试匹配不仅仅是最低限度(毕竟它是懒惰的)。\w*?
a b c d e
└┘ └┘ └┘ └┘ └┘ └┘
在这种情况下,匹配位于字符之间,使您具有以下子字符串:
"", "a", "b", "c", "d", "e", ""
不过,Java会抛弃尾随的空尾。让我们打破每次对String#split(String)
的调用。从Java文档中注意到“方法的工作原理就好像通过使用给定的表达式调用the two-argument split method并且限制参数为零。因此,尾随的空字符串不包含在结果数组中。”
"abcde".split("[a-z]"); // => []
这个匹配每个字符(a,b,c,d,e)并且只产生它们之间的空字符串,这些字符串被省略。
"abcde".split("\\w")); // => []
同样,字符串中的每个字符都是一个单词字符(\w
),因此结果是空字符串,省略。
"abcde".split("\\w*?")); // => ["", "a", "b", "c", "d", "e"]
在这种情况下,*
表示“前面项目中的零个或多个”(\w
),它与空表达式匹配七次(一次在字符串的开头,然后在每个字符之间一次)。所以我们得到第一个空字符串然后每个字符。
"abcde".split("\\w+?")); // => []
这里+
表示“前面的一个或多个项目”(\w
),它匹配整个输入字符串,只产生空字符串,省略。
使用input.split(regex, -1)
再次尝试这些示例,您应该看到所有空字符串。
String.split
在模式的每个匹配中剪切字符串:
此方法返回的数组包含此字符串的每个子字符串,该子字符串由与给定表达式匹配的另一个子字符串终止,或者由字符串的结尾终止。
因此,只要匹配[a-z]
这样的模式,就会在该匹配时剪切字符串。由于字符串中的每个字符都与模式匹配,因此结果数组为空(删除尾随空字符串)。
这同样适用于\w
和\w+?
(一个或多个\w
,但尽可能少重复)。那个\w*?
导致你预期的东西是由于*?
量词,因为如果可能的话,它将匹配零重复,所以一个空字符串。并且在给定字符串中的每个位置都找到一个空字符串。