String.split(regex) 是日常高頻 API,但其預設行為會丟棄尾部空欄位,導致 "##".split("#") 返回長度 0 而非 3 的反直覺結果。本文深入解析源碼機制,提供三種標準解決方案與最佳實踐。
System.out.println("1#2#3".split("#").length); // 3 ✓
System.out.println("##".split("#").length); // 0 ✗ (預期 3: ["", "", ""])
System.out.println("1##".split("#").length); // 1 ✗ (預期 3: ["1", "", ""])
String.split(String regex) 等價於 Pattern.compile(regex).split(input, 0)。
Pattern.split(CharSequence input, int limit) 中 limit=0 觸發尾部空欄位移除邏輯 (見 JDK 源碼 java.util.regex.Pattern.split)。
// split(regex, limit) limit < 0 表示不限制次數、保留尾部空欄位
"##".split("#", -1); // ["", "", ""] length=3 ✓
"1##".split("#", -1); // ["1", "", ""] length=3 ✓
"a,b,c,".split(",", -1); // ["a", "b", "c", ""] length=4 ✓
String str = "##";
str = str.replaceAll("#", "v^#v^"); // 佔位符包裹
String[] arr = str.split("#");
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i].replaceAll("v\\^", ""); // 移除佔位符
}
// arr = ["", "", ""]
// StringTokenizer 不丟棄空欄位,但不支援正則
StringTokenizer st = new StringTokenizer("##", "#", true); // true=保留分隔符
// 需自行組裝邏輯,較繁瑣,不推薦
若分隔符可能出現在引號內 (如 CSV),split 無法正確處理,建議:
Apache Commons CSV、OpenCSV 專業庫。(? (需支援 PCRE 的引擎,Java 原生不支援 (*SKIP)),實務上**務必用庫**。split(regex, -1):除非確定不需要尾部空欄位,否則預設加 -1。String.indexOf + substring 手寫:效能比正則快 10 倍以上,且行為可控。split("\\.")、split("\\|")、split("\\$") 等。split 丟棄尾部空欄位是設計決策而非 Bug (limit=0 語義)。牢記 split(regex, -1) 保留所有欄位,配合單字符手寫分割優化效能,即可徹底規避此類坑點。