混淆来自String.split的输出
我不明白这个代码的输出:
public class StringDemo{ public static void main(String args[]) { String blank = ""; String comma = ","; System.out.println("Output1: "+blank.split(",").length); System.out.println("Output2: "+comma.split(",").length); } }
并得到以下输出:
Output1: 1 Output2: 0
文档:
对于: System.out.println("Output1: "+blank.split(",").length);
此方法返回的数组包含此string的每个子string,该string由与给定expression式匹配的另一个子string终止,或者由string的末尾终止。 数组中的子string按照它们在此string中出现的顺序排列。 如果expression式不匹配input的任何部分,那么结果数组只有一个元素,即这个string 。
它将简单地返回整个string,这就是为什么它返回1。
对于第二种情况, String.split
将放弃,
所以结果将是空的。
String.split silently discards trailing separators
请参阅番石榴StringsExplained也
一切都按计划进行,但我们一步一步做(我希望你有一些时间)。
根据split(String regex)
方法的文档 (和源代码 ):
此方法的工作原理与通过调用给定expression式和极限参数为零的双参数拆分方法一样。
所以当你调用
split(String regex)
你实际上是从split(String regex, int limit)
方法调用的方式:
split(regex, 0)
所以这里limit
设置为0
。
你需要知道关于这个参数的一些事情:
- 如果
limit
是肯定的,你将结果数组的长度限制为你指定的正数,所以"axaxaxaxa".split("x",2)
将返回一个数组["a", "axaxaxa"]
,而不是["a","a","a","a","a"]
。 -
如果
limit
是0
那么你不限制结果数组的长度。 但是这也意味着任何尾随的空string都将被删除。 例如:"fooXbarX".split("X")
将在开始时生成一个数组,如下所示:
["foo", "bar", ""]
(
"barX"
在"X"
上分割会生成"bar"
和""
),但是由于split
会删除所有尾随的空string,它将返回["foo", "bar"]
-
limit
的负值的行为类似于限制设置为0
行为(它不会限制结果数组的长度)。 唯一的区别是它不会从结果数组的末尾删除空string。 换一种说法"fooXbarX".split("X",-1)
将返回["foo", "bar", ""]
让我们来看看这个案子,
",".split(",").length
(如前所述)与之相同
",".split(",", 0).length
这意味着我们正在使用一个不会限制结果数组长度的拆分版本,但会删除所有尾随的空string ""
。 你需要明白,当我们分裂一件事时,我们总是得到两件事情。
换句话说,如果我们拆分"abc"
来代替b
,我们会得到"a"
和"c"
。
棘手的部分是要明白,如果我们在c
分割"abc"
,我们将得到"ab"
和""
(空string)。
使用这个逻辑,如果我们将","
分开,
我们将得到""
和""
(两个空string)。
您可以使用负极限split
来检查它:
for (String s: ",".split(",", -1)){ System.out.println("\""+s+"\""); }
将打印
"" ""
所以我们看到结果数组在这里首先是["", ""]
。
但是由于默认情况下我们使用limit
设置为0
,所有尾随的空string将被删除。 在这种情况下,结果数组只包含尾随的空string ,因此所有这些都将被删除 ,留下长度为0
空数组[]
。
用来回答这个问题
"".split(",").length
你需要明白, 只有当这样的尾随空string是分裂结果的时候 , 删除尾随的空string才有意义(而且很可能不需要) 。
所以如果没有可以拆分的地方,就不可能创build空的string,所以运行这个“清理”过程没有任何意义。
在split(String regex, int limit)
方法的文档中提到了这个信息,你可以阅读:
如果expression式不匹配input的任何部分,那么结果数组只有一个元素,即这个string 。
你也可以在这个方法的源代码中看到这个行为(来自Java 8):
2316 public String [] split( String regex, int limit){
2317 / *快速path,如果正则expression式是
2318 (1)单string和这个字符不是其中之一
2319 RegEx的元字符“。$ |()[{^?* + \\”,或者
2320 (2)两个string和第一个字符是反斜杠和
2321第二个不是ascii数字或ascii字母。
2322 * /
2323 char ch = 0;
2324 if (((regex.value.length == 1 &&
2325 “。$ |()[{^?* + \\”。 indexOf (ch = regex。charAt (0))== -1)||
2326 (正则expression式。length ()== 2 &&
2327正则expression式。 charAt (0)=='\\'&&
2328 (((ch = regex。charAt (1)) - '0')|('9'-ch))<0 &&
2329 ((ch-'a')|('z'-ch))<0 &&
2330 ((ch -'A')|('Z'-ch))<0))&&
2331 (ch <Character.MIN_HIGH_SURROGATE ||
2332 ch> Character.MAX_LOW_SURROGATE))
2333 {
2334 int off = 0;
2335 int next = 0;
2336 布尔限制=限制> 0;
2337 ArrayList < String > list = new ArrayList <>();
((next = indexOf (ch,off))!= -1){
2339 if (!limited || list。size ()<limit - 1){
2340名单。 add ( substring (off,next));
2341 off = next + 1;
2342 } 其他 {//最后一个
2343 // assert(list.size()== limit - 1);
2344名单。 add ( substring (off,value.length));
2345 off = value.length;
2346 打破 ;
2347 }
2348 }
2349 //如果找不到匹配, 请将其返回
2350 if (off == 0)
2351 return new String [] { this };
2353 //添加剩余的段
2354 if (!limited || list。size ()<limit)
2355名单。 add ( substring (off,value.length));
2357 //构造结果
2358 int resultSize = list。 size ();
2359 if (limit == 0){
2360 while (resultSize> 0 && list。get(resultSize - 1)。length()== 0){
2361结果大小 - ;
2362 }
2363 }
2364 String [] result = new String [resultSize];
2365 退货清单。 subList (0,resultSize)。 toArray (result);
2366 }
2367 返回模式。 编译 (正则expression式)。 分裂 ( 这个 ,限制);
2368 }
在那里你可以find
if (off == 0) return new String[]{this};
这意味着片段
-
if (off == 0)
– 如果off
(从哪个方法开始search下一个可能匹配的正则expression式作为split
parameter passing的位置)在迭代整个string后仍然为0
,我们没有find任何匹配,所以string是不分裂 -
return new String[]{this};
– 在这种情况下,让我们只返回一个原始string的数组(this
表示)。
由于","
甚至一次都不能在""
find, "".split(",")
必须返回一个包含一个元素(调用split
空string"".split(",")
的数组。 这意味着这个数组的长度是1
。
BTW。 Java 8引入了另一种机制。 如果我们使用零长度正则expression式 (比如""
或环视(?<!x)
)分割,它将删除前导空string(如果它们是在分割过程中创build的 )。 更多信息: 为什么在Java 8中,split有时会在结果数组开始时删除空string?
从Java 1.7文档
将string拆分为给定正则expression式的匹配。
split()方法的工作原理就好像通过调用具有给定expression式和极限参数为零的双参数拆分方法。 尾随的空string因此不包括在结果数组中。
在情况1中, blank.split(",") does not match any part of the input then the resulting array has just one element, namely this String.
It will return entire String.
所以,长度将是1
。
在情况2中, comma.split(",") will return empty.
split()
期待一个正则expression式作为参数,返回结果数组来匹配那个正则expression式。
所以长度是0
例如 ( 文档 )
string“boo:and:foo”用这些expression式产生以下结果:
Regex Result : { "boo", "and", "foo" } o { "b", "", ":and:f" }
参数: regex – 分隔正则expression式
返回:通过将该string分割为给定正则expression式的匹配而计算的string数组
抛出: PatternSyntaxException – 如果正则expression式的语法无效
从String类的javadoc中为public String[] split (String regex)
方法:
将该string拆分为给定正则expression式的匹配。
此方法的工作原理与通过调用给定expression式和极限参数为零的双参数拆分方法一样。 尾随的空string因此不包括在结果数组中。
在第一种情况下,expression式不匹配input的任何部分,所以我们得到一个只有一个元素的数组 – input。
在第二种情况下,匹配input和split的expression式应该返回两个空string; 但根据javadoc,他们被丢弃(因为他们是尾随和空的)。
我们可以看看String.split
后面的java.util.regex.Pattern
的源代码 。 一路顺着兔子洞的方法
public String[] split(CharSequence input, int limit)
被调用。
input""
对于input""
这个方法被称为
String[] parts = split("", 0);
这种方法的中间部分是 :
int index = 0; boolean matchLimited = limit > 0; ArrayList<String> matchList = new ArrayList<>(); Matcher m = matcher(input); while(m.find()) { // Tichodroma: this will not happen for our input } // If no match was found, return this if (index == 0) return new String[] {input.toString()};
这就是发生的事情:返回new String[] {input.toString()}
。
input","
对于“input","
input","
部分是
// Construct result int resultSize = matchList.size(); if (limit == 0) while (resultSize > 0 && matchList.get(resultSize-1).equals("")) resultSize--; String[] result = new String[resultSize]; return matchList.subList(0, resultSize).toArray(result);
这里的resultSize == 0
和limit == 0
所以返回new String[0]
。
从JDK 1.7
public String[] split(String regex, int limit) { /* fastpath if the regex is a (1)one-char String and this character is not one of the RegEx's meta characters ".$|()[{^?*+\\", or (2)two-char String and the first char is the backslash and the second is not the ascii digit or ascii letter. */ char ch = 0; if (((regex.count == 1 && ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) || (regex.length() == 2 && regex.charAt(0) == '\\' && (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 && ((ch-'a')|('z'-ch)) < 0 && ((ch-'A')|('Z'-ch)) < 0)) && (ch < Character.MIN_HIGH_SURROGATE || ch > Character.MAX_LOW_SURROGATE)) { int off = 0; int next = 0; boolean limited = limit > 0; ArrayList<String> list = new ArrayList<>(); while ((next = indexOf(ch, off)) != -1) { if (!limited || list.size() < limit - 1) { list.add(substring(off, next)); off = next + 1; } else { // last one //assert (list.size() == limit - 1); list.add(substring(off, count)); off = count; break; } } // If no match was found, return this if (off == 0) return new String[] { this }; // Add remaining segment if (!limited || list.size() < limit) list.add(substring(off, count)); // Construct result int resultSize = list.size(); if (limit == 0) while (resultSize > 0 && list.get(resultSize-1).length() == 0) resultSize--; String[] result = new String[resultSize]; return list.subList(0, resultSize).toArray(result); } return Pattern.compile(regex).split(this, limit); }
所以对于这种情况,正则expression式将由第一个if
处理。
对于第一种情况, blank.split(",")
// If no match was found, return this if (off == 0) return new String[] { this };
所以,如果没有匹配,这个函数将返回一个包含一个元素的数组。
对于第二种情况comma.split(",")
List<String> list = new ArrayList<>(); //... int resultSize = list.size(); if (limit == 0) while (resultSize > 0 && list.get(resultSize-1).length() == 0) resultSize--; String[] result = new String[resultSize]; return list.subList(0, resultSize).toArray(result);
正如你注意到的,最后一个while循环已经移除了列表末尾的所有空元素,所以resultSize是0
。
String blank = ""; String comma = ","; System.out.println("Output1: "+blank.split(",").length); // case 1 System.out.println("Output2: "+comma.split(",").length); // case 2
情况1 – 这里blank.split(",")
将返回""
因为没有,
在blank
你得到相同的,所以长度将是1
情况2-这里comma.split(",")
将返回空数组,你必须scape ,
如果你想计数长度为1
comma
,否则长度将为0
再次comma.split(",")
拆分()期待一个regex
作为参数它将返回结果数组匹配与该regex
。
此方法返回的数组包含此string的每个子string,该string由与给定expression式匹配的另一个子string终止,或者由string的末尾终止。
其他
如果expression式不匹配input的任何部分,那么结果数组只有一个元素,即这个string。
split方法的API声明:“如果expression式不匹配input的任何部分,那么结果数组只有一个元素,即这个string。
因此,由于String空白不包含“,”,所以返回一个带有一个元素(即空白本身)的String []。
对于string逗号,原始string的“无”是空的,因此返回一个空数组。
这似乎是最好的解决scheme,如果你想处理返回的结果,例如
String[] splits = aString.split(","); for(String split: splits) { // do something }