混淆来自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"]
  • 如果limit0那么你不限制结果数组的长度。 但是这也意味着任何尾随的空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式作为splitparameter 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 == 0limit == 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 }