令人困惑的String.split输出

时间:2014-07-31 10:28:16

标签: java regex string class core

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

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

8 个答案:

答案 0 :(得分:54)

文档:

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

  

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

它只会返回整个字符串,这就是它返回1的原因。


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

String.split silently discards trailing separators

另见guava StringsExplained

答案 1 :(得分:34)

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

根据split(String regex)方法的documentation(和source code):

  

此方法的工作方式就像调用带有给定表达式和limit参数为零的双参数split方法一样。

所以当你调用

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"]。< / LI>
  • 如果limit0,那么您不会限制结果数组的长度。但它也意味着将删除任何尾随的空字符串。例如:

    "fooXbarX".split("X")
    

    将在开始时生成一个类似于:

    的数组
    ["foo", "bar", ""]
    

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

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

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

将返回["foo", "bar", ""]


让我们来看看案例,

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

(如前所述)与

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

这意味着我们使用的split版本不会限制结果数组的长度,但 将删除所有尾随的空字符串 ,{{1} }。你需要明白,当我们分割一个时,我们总是得到两个的东西。

换句话说,如果我们将""分开"abc",我们将获得b"a"。 棘手的部分是了解如果我们在"c"中拆分"abc",我们将获得c"ab"(空字符串)。

使用此逻辑,如果我们在""上分割",",我们将获得,""(两个空字符串)。

您可以使用带有否定限制的""进行检查:

split

将打印

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

因为我们在这里看到结果数组最初是"" ""

但是默认情况下我们使用["", ""]设置为limit,所有尾随空字符串都将被删除。在这种情况下,结果数组包含仅尾随空字符串,因此将删除所有 ,留下空数组0,其长度为{{ 1}}。


回答案件
[]

你需要明白删除尾随的空字符串只有在这样的尾随空字符串拆分的结果<(并且很可能不需要)时才有意义。 > 因此,如果没有我们可以分割的任何地方,就不可能创建空字符串,因此运行此“清理”过程没有意义。

此信息在documentation of split(String regex, int limit)方法中提及,您可以阅读:

  

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

您还可以在source code of this method(来自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 }

你可以在哪里找到

0

片段意味着

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

由于在0中找不到return new String[]{this};一次,this必须返回一个包含一个元素的数组(您调用的空字符串",")。这意味着此数组的长度为""

顺便说一句。 Java 8引入了另一种机制。如果我们使用zero-length regex(例如"".split(",")或使用环顾split)进行拆分,它会删除前导空字符串(如果它们在拆分过程中创建)。更多信息:Why in Java 8 split sometimes removes empty strings at start of result array?

答案 2 :(得分:7)

来自Java 1.7 Documentation

  

将字符串拆分为给定正则表达式的匹配项。

     

split()方法的工作方式就像调用带有给定表达式和limit参数为零的双参数split方法一样。 结尾的空字符串因此不包含在结果数组中。

案例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()期望正则表达式作为参数,返回结果数组以匹配该正则表达式。

因此,长度为0

例如Documentation

  

字符串“boo:and:foo”,使用以下表达式产生以下结果:

Regex     Result
  :     { "boo", "and", "foo" }
  o     { "b", "", ":and:f" }
  

<强>参数:      正则表达式 - 分隔正则表达式

     

<强>返回:      通过将此字符串拆分为给定正则表达式

的匹配项来计算的字符串数组      

<强>抛出:      PatternSyntaxException - 如果正则表达式的语法无效

答案 3 :(得分:4)

来自public String[] split(String regex)方法的字符串类javadoc

  

围绕给定正则表达式的匹配拆分此字符串。

     

此方法的工作方式就像调用带有给定表达式和limit参数为零的双参数split方法一样。因此,尾随空字符串不包含在结果数组中。

在第一种情况下,表达式与输入的任何部分都不匹配,所以我们得到一个只有一个元素的数组 - 输入。

在第二种情况下,表达式匹配input和split应该返回两个空字符串;但是,根据javadoc,它们被丢弃(因为它们是尾随和空的)。

答案 4 :(得分:3)

我们可以查看String.split后面的the source code of java.util.regex.Pattern。顺着兔子洞的方法

public String[] split(CharSequence input, int limit)

被调用。

输入""

对于输入"",此方法称为

String[] parts = split("", 0);

The intersting part of this method is

  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()} is returned

输入","

输入"," the intersting part is

    // 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] is returned

答案 5 :(得分:2)

来自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);
    }

因此,对于这种情况,正则表达式将由第一个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

答案 6 :(得分:1)

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(",")将返回空数组,如果要计算,长度为comma,则必须使用1,否则长度为{{1} }}

再次0 split()期望comma.split(",")作为参数,它将返回结果数组以匹配regex

  

此方法返回的数组包含此子字符串   由与给定匹配的另一个子字符串终止的字符串   表达式或由字符串的结尾终止。

否则

  

如果表达式与输入的任何部分都不匹配,那么   结果数组只有一个元素,即这个字符串。

答案 7 :(得分:1)

split方法的API指出“如果表达式与输入的任何部分都不匹配,那么结果数组只有一个元素,即此字符串。”

因此,由于String空白不包含“,”,因此返回带有一个元素(即空白本身)的String []。

对于String逗号,原始字符串中没有“nothing”,因此返回一个空数组。

如果你想处理返回的结果,这似乎是最好的解决方案,例如:克。

String[] splits = aString.split(",");
for(String split: splits) {
   // do something
}