源码--JAVA中String的split方法 2020-06-18 15:34 > 以下源码版本:JDK1.8 ### 简介 Java 中 String 的 split 方法可以将字符串根据指定的间隔进行切割,例如字符串 str = "1,23,4,,5" 经过 str.split(",") 切割后得到的返回值是一个字符串数组 String[] = [1, 23, 4, , 5],这种处理方式可以适配大多数场景。 ### 问题 今天写一个读取csv文件的时候,发现一个小问题。【csv,一种文本文件格式,每行中的数据以逗号分隔,在windows平台可以使用excel打开,打开后和普通excel显示并无差别】 我处理的文件中存在这样的一行数据: ```csv ,,,,f02e843b-a328-4023-bbbd-7ce075a29bef,,,2.00422E+19,,3000,12,6,2020/10/22,1,245.05,56.34,0,301.39,245.05,56.34,0,0,301.39,0,0,0,0,,0,0,NULL,,,,,,,,,,,,,,,,,,,, ``` 使用`BufferedReader.readLine()`去读取一行,然后用逗号分隔:`str.split(",")`,最后发现拿到 的字符串数组中,前面的空串数据存在,后面的空串数据都消失了。即split切割后剩下的数据: ```csv ,,,,f02e843b-a328-4023-bbbd-7ce075a29bef,,,2.00422E+19,,3000,12,6,2020/10/22,1,245.05,56.34,0,301.39,245.05,56.34,0,0,301.39,0,0,0,0,,0,0,NULL ``` (后面的空串没了,到NULL结束了) 由于csv文件中,每列数据都对应的有key,例如第一列为id,第二列为name,等等,所以即使是空串也必须存在,不能丢失。 ### 分析 在看过split源码后发现,String的split方法有两个重载方法:`public String[] split(String regex)` 和 `public String[] split(String regex, int limit)` 而调用`split(String regex)`方法时,内部是直接调用的 `split(regex, 0)`,如下图: 【这里原本有一张图片, id: 80f5353c-a640-4a99-a645-f2e04298f75e.jpg 】 在`split(regex, 0)`中,进行字符串的切割。 源码里,切割后又做了其他事情,如下: ```java int resultSize = list.size(); if (limit == 0) { while (resultSize > 0 && list.get(resultSize - 1).length() == 0) { resultSize--; } } ``` 其中`list`储存结果数据。 如果我们使用`split(regex)`的话,会调用`split(regex,0)`那么就是说我们不传入limit参数,limit默认为0。所以这里会进入if语句。 进入后,就开始从后向前循环(resultSize为结果元素长度,所以是从后向前),如果是空串(length==0),就清除掉此数据(resultSize--)。 注意由于这里是 && 符号连接,所以当循环时遇到不为空串,就立即跳出while了。所以此过程清除掉的数据是结尾处的所有空串。 `,,,a,bss,cas,,fq,try,,h,,,,` 这个字符串使用`split(",")`切割后,留下的数据是`,,,a,bss,cas,,fq,try,,h`,在从后向前循环时碰到第一个不为空串的数据 h 就会停止,然后返回。 而我们想要让split不清除数据,拿到分隔后的原始数据时怎么办呢?看代码知道应该传入limit,让limit!=0,这样就不会进入if语句,不会执行这些清除空串数据的代码。那么limit的值应该传入什么呢?下面进行几个小实验: ```java # 1、 String str = ",,,a,bss,cas,,fq,try,,h,,,,"; String[] split2 = str.split(",", 1); # limit为1 System.out.println(Arrays.toString(split2)); System.out.println("长度为"+split2.length); >[,,,a,bss,cas,,fq,try,,h,,,,] # 此处是一个串 >长度为1 # 2、 String str = ",,,a,bss,cas,,fq,try,,h,,,,"; String[] split2 = str.split(",", 5); # limit为5 System.out.println(Arrays.toString(split2)); System.out.println("长度为"+split2.length); >[, , , a, bss,cas,,fq,try,,h,,,,] # 此处是5个串,分别为:空串,空串,空串,a,bss,cas,,fq,try,,h,,,, >长度为5 # 3、 String str = ",,,a,bss,cas,,fq,try,,h,,,,"; String[] split2 = str.split(",", Integer.MAX_VALUE); # limit为Integer.MAX_VALUE System.out.println(Arrays.toString(split2)); System.out.println("长度为"+split2.length); >[, , , a, bss, cas, , fq, try, , h, , , , ] # 此处将所有串都拿到了,没有清除数据。 >长度为15 ``` 当传入负数时,偶尔会运行成功,大多数情况下,会报以下错误: 【这里原本有一张图片, id: 5e00a034-469e-4e6c-b250-9262a2b10402.jpg 】 ### 结论 我们可以分析出,传入limit为正数时,split就不会帮我们清除掉它认为无用的数据,而是将limit作为结果长度的最大值进行切割。而当传入limit为0时,split会帮我们切割掉后面的空串。**所以limit的含义是切割后结果的阀值(最大值),帮我们限制切割后结果的长度,limit为0时就走JAVA自己定的规则(清除结尾处的空串)。** 所以当我们的使用场景需要全部数据时,要使用两个参数的split方法手动传入limit,建议limit的值为Integer.MAX_VALUE,这样可以在不知道结果长度的情况下保证结果正确。 `split(regex,limit)`方法的完整结构大概可分三段解释,如图: 【这里原本有一张图片, id: b9e00679-1e00-414a-8478-f0d1ef463f28.jpg 】 --END--
发表评论