String.split()方法你可能不知道的一面
String.split()方法你可能不知道的一面
一、問題
java中String的split()是我們經常使用的方法,用來按照特定字符分割字符串,那么我們看以下一段代碼:
public void splitTest() {
String str = "aaa|bbb|ccc";
String[] array = str.split("|");
System.out.println(Arrays.toString(array));
}
是不是感覺很簡單,就是吧str按照"|"分割,結果就是[aaa,bbb,ccc]嘛。如果你這么想,那么以后在用這個方法時你可能會犯下大錯,把程序跑起來,你會驚訝的發現程序輸入如下結果:
[, a, a, a, |, b, b, b, |, c, c, c]
為什么會出現這種情況呢?我們再來運行一下str.split("");就會發現結果和之前的結果一樣,也就是說我們使用"|"分割的時候其實split是按照空字符分割的。
二、探究
為了找到原因,我們在來使用其他幾種字符測試一下,結果如下:
0:[aaa, bbb, ccc]
$:[aaa$bbb$ccc]
,:[aaa, bbb, ccc]
*:異常java.util.regex.PatternSyntaxException: Dangling meta character '*' near index 0
^:[aaa^bbb^ccc]
我們會發現對于數字,","結果是正確的,而對于特殊字符,結果都不正確,而且如果經驗豐富,你可能已經發現,這些特殊字符都是正則表達式中的匹配符,而那個異常也很明確的說明了這一點。
因此,我們可以猜測在split內部使用了正則表達式來匹配并分割字符串。那么現在又有一個疑問:我們知道一般情況下String涉及的字符串處理多數使用indexOf()(可能是考慮效率問題吧),那么這里為什么使用正則呢?我們還是直接看看源碼吧:
public String[] split(String regex, int limit) {
char ch = 0;
if (((regex.value.length == 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, value.length));
off = value.length;
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, value.length));
// 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,如果條件為true,那么就使用indexOf()判斷后substring()截取,如果為false,則使用正則處理。那么我們就來分析下這個if的條件:
//第一步部分:當regex的長度為1且不是".$|()[{^?*+\\"中的時,為真
(regex.value.length == 1 &&".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1)
//第二部分:當長度為2時且第一個字符為"\"轉義字符,第二個字符不是字符0-9 a-z A-Z 以及utf-16之間的字符
(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) www.tygj123.com
從if可以看出如果regex內容為一個非正則匹配符或者是轉以后的特殊字符時,采用indexOf()+substring()處理,否則使用正則表達式。
那么為什么這么做呢,直接使用第一種方法不就行了?其實我們可以考慮一種復雜的情況:
aaax111xbbbx222xcccx333xddd
如果我想分割出這樣的結果 [aaa, bbb, ccc, ddd] 應該則么做呢?按照第一種方法,實現起來很麻煩,需要一大堆判斷,反而不如正則方便,而String中的split()方法正是出于這樣的考慮實現的,不信你用split("x.*?x")試試。
三、啟示
這次這件事雖小,但是卻讓我收獲不少。
1、使用一個不了解的方法前,一定要看一眼提示,split方法說明里雖然沒有明確的提示上述問題,但是多個地方都提到了正則,連參數名字都是regex,作為一個經驗豐富的程序員,應該想到這一點。
2、對于一些小的功能點(往往我們還胸有成竹)一定要寫個Test測一下,其實把代碼貼過去跑一下費不了多少勁,但是當他淹沒在成百上千行的代碼中時,想要在發現問題就很費勁了,特別是你不調試根本想不到這點會錯 www.yztrans.com
3、我們在多數項目中都要建個util工具包,封裝各種輔助的操作類,然而這些類的功能往往只是滿足當時的需求,之后用到類似但有變動的功能時往往習慣單獨實現一個,而不是實現一個完善的覆蓋原來的,從設計的角度會造成接口不一致:明明同樣的需求、同樣的參數,我卻還要看看他的內部實現才能選擇用哪個。對于這些工具類,我們不一定要非常完美,但是應該用心做到盡量完善,這也方便復用。
轉載于:https://www.cnblogs.com/haosola/p/3655733.html
總結
以上是生活随笔為你收集整理的String.split()方法你可能不知道的一面的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 遇到个鬼,在WIN08的DELL R71
- 下一篇: 阻止浏览器关闭 区分刷新和关闭 自试I