我的個人網站:
http://riun.xyz
以下源碼版本:JDK1.8
簡介
Java 中 String 的 split 方法可以將字符串根據指定的間隔進行切割,例如字符串 str = “1,23,4,5” 經過 str.split(",") 切割後得到的返回值是一個字符串數組 String[] = [1, 23, 4, , 5],這種處理方式可以適配大多數場景。
問題
今天寫一個讀取csv文件的時候,發現一個小問題。【csv,一種文本文件格式,每行中的數據以逗號分隔,在windows平臺可以使用excel打開,打開後和普通excel顯示並無差別】
我處理的文件中存在這樣的一行數據:
,,,,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切割後剩下的數據:
,,,,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)
,如下圖:
在split(regex, 0)
中,進行字符串的切割。
源碼裏,切割後又做了其他事情,如下:
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的值應該傳入什麼呢?下面進行幾個小實驗:
# 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
當傳入負數時,偶爾會運行成功,大多數情況下,會報以下錯誤:
結論
我們可以分析出,傳入limit爲正數時,split就不會幫我們清除掉它認爲無用的數據,而是將limit作爲結果長度的最大值進行切割。而當傳入limit爲0時,split會幫我們切割掉後面的空串。所以limit的含義是切割後結果的閥值(最大值),幫我們限制切割後結果的長度,limit爲0時就走JAVA自己定的規則(清除結尾處的空串)。
所以當我們的使用場景需要全部數據時,要使用兩個參數的split方法手動傳入limit,建議limit的值爲Integer.MAX_VALUE,這樣可以在不知道結果長度的情況下保證結果正確。
split(regex,limit)
方法的完整結構大概可分三段解釋,如圖: