来自 String.split 的令人困惑的输出

2022-08-31 20:10:34

我不明白这个代码的输出:

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

答案 1

文档:

为:System.out.println("Output1: "+blank.split(",").length);

此方法返回的数组包含此字符串的每个子字符串,该子字符串由与给定表达式匹配的另一个子字符串终止,或者由字符串末尾终止。数组中的子字符串按它们在此字符串中出现的顺序排列。如果表达式与输入的任何部分都不匹配,则生成的数组只有一个元素,即此字符串

它将简单地返回整个字符串,这就是它返回1的原因。


对于第二种情况,将丢弃 ,因此结果将为空。String.split,

String.split silently discards trailing separators

看到番石榴字符串也解释


答案 2

一切都按计划进行,但让我们一步一步地去做(我希望你有时间)。

根据方法的文档(和源代码):split(String regex)

此方法的工作方式就像调用具有给定表达式和极限参数零的双参数拆分方法一样。

因此,当您调用时

split(String regex)

您实际上是从以某种方式调用的方法中获得结果的:split(String regex, int limit)

split(regex, 0)

所以这里设置为 .limit0

您需要了解有关此参数的一些信息:

  • 如果为正数,则将结果数组的长度限制为指定的正数,因此将返回数组 , 而不是 。limit"axaxaxaxa".split("x",2)["a", "axaxaxa"]["a","a","a","a","a"]
  • 如果 是,则不会限制结果数组的长度。但这也意味着任何尾随的空字符串都将被删除。例如:limit0

    "fooXbarX".split("X")
    

    将在开始时生成一个数组,如下所示:

    ["foo", "bar", ""]
    

    ("barX"在生成和 )时拆分,但由于删除了所有尾随的空字符串,它将返回"X""bar"""split

    ["foo", "bar"]
    
  • 负值的行为类似于限制设置为的行为(它不会限制结果数组的长度)。唯一的区别是它不会从结果数组的末尾删除空字符串。换句话说limit0

    "fooXbarX".split("X",-1)
    

会再来["foo", "bar", ""]


让我们来看看这个案例,

",".split(",").length

(如前所述)与

",".split(",", 0).length

这意味着我们正在使用一个版本的split,它不会限制结果数组的长度,但会删除所有尾随的空字符串,.你需要明白,当我们把件事分开时,我们总是得到件事。""

换句话说,如果我们拆分代替 ,我们将得到 和 。
棘手的部分是要明白,如果我们拆分进去,我们将得到和(空字符串)。"abc"b"a""c""abc"c"ab"""

使用此逻辑,如果我们拆分,我们将得到 和 (两个空字符串)。",",""""

您可以使用负限制进行检查:split

for (String s: ",".split(",", -1)){
    System.out.println("\""+s+"\"");
}

这将打印

""
""

因此,正如我们所看到的,这里的结果数组首先是 。["", ""]

但是由于默认情况下我们使用 set to ,所有尾随的空字符串都将被删除。在这种情况下,结果数组仅包含尾随的空字符串,因此所有这些字符串都将被删除,从而留下长度为的空数组。limit0[]0


要回答案例

"".split(",").length

您需要了解,仅当此类尾随空字符串处理拆分结果(并且很可能不需要)时,删除尾随空字符串才有意义
因此,如果没有任何可以拆分的地方,则不可能创建空字符串,因此运行此“清理”过程没有意义。

此信息在 split(String regex, int limit) 方法的文档中提到,您可以在其中阅读:

如果表达式与输入的任何部分都不匹配,生成的数组只有一个元素,即此字符串

您还可以在此方法的源代码(来自 Java 8)中看到此行为:

2316      public String[] split(String regex, int limit) {
2317 /* fastpath if the regex is a
2318 (1)one-char String and this character is not one of the
2319 RegEx's meta characters ".$|()[{^?*+\\", or
2320 (2)two-char String and the first char is the backslash and
2321 the second is not the ascii digit or ascii letter.
2322 */
2323 char ch = 0;
2324 if (((regex.value.length == 1 &&
2325 ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
2326 (regex.length() == 2 &&
2327 regex.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 boolean limited = limit > 0;
2337 ArrayList<String> list = new ArrayList<>();
2338 while ((next = indexOf(ch, off)) != -1) {
2339 if (!limited || list.size() < limit - 1) {
2340 list.add(substring(off, next));
2341 off = next + 1;
2342 } else { // last one
2343 //assert (list.size() == limit - 1);
2344 list.add(substring(off, value.length));
2345 off = value.length;
2346 break;
2347 }
2348 }
2349 // If no match was found, return this
2350 if (off == 0)
2351 return new String[]{this};
2353 // Add remaining segment
2354 if (!limited || list.size() < limit)
2355 list.add(substring(off, value.length));
2357 // Construct result
2358 int resultSize = list.size();
2359 if (limit == 0) {
2360 while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
2361 resultSize--;
2362 }
2363 }
2364 String[] result = new String[resultSize];
2365 return list.subList(0, resultSize).toArray(result);
2366 }
2367 return Pattern.compile(regex).split(this, limit);
2368 }

在哪里可以找到

if (off == 0)
    return new String[]{this};

片段,这意味着

  • if (off == 0)- 如果(位置应该从哪个方法开始搜索作为参数传递的正则表达式的下一个可能匹配项)迭代整个字符串后仍然,我们没有找到任何匹配项,因此字符串未被拆分offsplit0
  • return new String[]{this};- 在这种情况下,让我们只返回一个包含原始字符串的数组(由 表示)。this

由于甚至一次都找不到,因此必须返回一个具有一个元素的数组(您调用的空字符串)。这意味着此数组的长度为 。",""""".split(",")split1

顺便说一句。Java 8 引入了另一种机制。如果我们使用零长度正则表达式(如或环顾四周)进行拆分,它将删除前导空字符串(如果它们是在拆分过程中创建的)。更多信息见:为什么在 Java 8 中,拆分有时会在结果数组的开头删除空字符串?""(?<!x)