講究!字符串拆分還能這么玩
“哥,我感覺字符串拆分沒什么可講的呀,直接上 String 類的 split() 方法不就可以了!”三妹毫不客氣地說。
“假如你真的這么覺得,那可要注意了,事情遠(yuǎn)沒這么簡(jiǎn)單。”我微笑著說。
假如現(xiàn)在有這樣一串字符序列“沉默王二,一枚有趣的程序員”,需要按照中文逗號(hào)“,”進(jìn)行拆分,這意味著第一串字符序列為逗號(hào)前面的“沉默王二”,第二串字符序列為逗號(hào)后面的“一枚有趣的程序員”。
“這不等于沒說嗎?哥!”還沒等我說,三妹就打斷了我。
“別著急嘛,等哥說完。”我依然保持著微笑繼續(xù)說,“在拆分之前,要先進(jìn)行檢查,判斷一下這串字符是否包含逗號(hào),否則應(yīng)該拋出異常。”
public class Test {
public static void main(String[] args) {
String cmower = "沉默王二,一枚有趣的程序員";
if (cmower.contains(",")) {
String [] parts = cmower.split(",");
System.out.println("第一部分:" + parts[0] +" 第二部分:" + parts[1]);
} else {
throw new IllegalArgumentException("當(dāng)前字符串沒有包含逗號(hào)");
}
}
}
“三妹你看,這段代碼挺嚴(yán)謹(jǐn)?shù)陌桑俊蔽艺f,“來看一下程序的輸出結(jié)果。”
第一部分:沉默王二 第二部分:一枚有趣的程序員
“的確和預(yù)期完全一致。”三妹說。
“這是建立在字符串是確定的情況下,最重要的是分隔符是確定的。否則,麻煩就來了。”我說,“大約有 12 種英文特殊符號(hào),如果直接拿這些特殊符號(hào)替換上面代碼中的分隔符(中文逗號(hào)),這段程序在運(yùn)行的時(shí)候就會(huì)出現(xiàn)以下提到的錯(cuò)誤。”
反斜杠 \(ArrayIndexOutOfBoundsException)插入符號(hào) ^(同上)美元符號(hào) $(同上)逗點(diǎn) .(同上)豎線 |(正常,沒有出錯(cuò))問號(hào) ?(PatternSyntaxException)星號(hào) *(同上)加號(hào) +(同上)左小括號(hào)或者右小括號(hào) ()(同上)左方括號(hào)或者右方括號(hào) [](同上)左大括號(hào)或者右大括號(hào) {}(同上)
“那遇到這些特殊符號(hào)該怎么辦呢?”三妹問。
“用正則表達(dá)式。”我說,“正則表達(dá)式是一組由字母和符號(hào)組成的特殊文本,它可以用來從文本中找出滿足你想要的格式的句子。”
我在 GitHub 上找打了一個(gè)開源的正則表達(dá)式學(xué)習(xí)文檔,非常詳細(xì)。一開始寫正則表達(dá)式的時(shí)候難免會(huì)感覺到非常生疏,你可以查看一下這份文檔。記不住沒關(guān)系,遇到就查。
https://github.com/cdoco/learn-regex-zh
除了這份文檔,還有一份:
https://github.com/cdoco/common-regex
作者收集了一些在平時(shí)項(xiàng)目開發(fā)中經(jīng)常用到的正則表達(dá)式,可以直接拿來用。
“哥,你真周到。”三妹笑著說。
“好了,來用英文逗點(diǎn) . 替換一下分隔符。”我說。
String cmower = "沉默王二.一枚有趣的程序員";
if (cmower.contains(".")) {
String [] parts = cmower.split("\\.");
System.out.println("第一部分:" + parts[0] +" 第二部分:" + parts[1]);
}
由于英文逗點(diǎn)屬于特殊符號(hào),所以在使用 split() 方法的時(shí)候,就需要使用正則表達(dá)式 \\. 而不能直接使用 .。
“為什么用兩個(gè)反斜杠呢?”三妹問。
“因?yàn)榉葱备鼙旧砭褪且粋€(gè)特殊字符,需要用反斜杠來轉(zhuǎn)義。”我說。
當(dāng)然了,你也可以使用 [] 來包裹住英文逗點(diǎn)“.”,[] 也是一個(gè)正則表達(dá)式,用來匹配方括號(hào)中包含的任意字符。
cmower.split("[.]");
除此之外, 還可以使用 Pattern 類的 quote() 方法來包裹英文逗點(diǎn)“.”,該方法會(huì)返回一個(gè)使用 \Q\E 包裹的字符串。

來看示例:
String [] parts = cmower.split(Pattern.quote("."));
當(dāng) split() 方法的參數(shù)是正則表達(dá)式的時(shí)候,方法最終會(huì)執(zhí)行下面這行代碼:
return Pattern.compile(regex).split(this, limit);
也就意味著,拆分字符串有了新的選擇,可以不使用 String 類的 split() 方法,直接用下面的方式。
public class TestPatternSplit {
private static Pattern twopart = Pattern.compile("\\.");
public static void main(String[] args) {
String [] parts = twopart.split("沉默王二.一枚有趣的程序員");
System.out.println("第一部分:" + parts[0] +" 第二部分:" + parts[1]);
}
}
“為什么要把 Pattern 表達(dá)式聲明稱 static 的呢?”三妹問。
“由于模式是確定的,通過 static 的預(yù)編譯功能可以提高程序的效率。”我說,“除此之外,還可以使用 Pattern 配合 Matcher 類進(jìn)行字符串拆分,這樣做的好處是可以對(duì)要拆分的字符串進(jìn)行一些嚴(yán)格的限制,來看這段示例代碼。”
public class TestPatternMatch {
/**
* 使用預(yù)編譯功能,提高效率
*/
private static Pattern twopart = Pattern.compile("(.+)\\.(.+)");
public static void main(String[] args) {
checkString("沉默王二.一枚有趣的程序員");
checkString("沉默王二.");
checkString(".一枚有趣的程序員");
}
private static void checkString(String str) {
Matcher m = twopart.matcher(str);
if (m.matches()) {
System.out.println("第一部分:" + m.group(1) + " 第二部分:" + m.group(2));
} else {
System.out.println("不匹配");
}
}
}
正則表達(dá)式 (.+)\\.(.+) 的意思是,不僅要把字符串按照英文標(biāo)點(diǎn)的方式拆成兩部分,并且英文逗點(diǎn)的前后要有內(nèi)容。
來看一下程序的輸出結(jié)果:
第一部分:沉默王二 第二部分:一枚有趣的程序員
不匹配
不匹配
不過,使用 Matcher 來匹配一些簡(jiǎn)單的字符串時(shí)相對(duì)比較沉重一些,使用 String 類的 split() 仍然是首選,因?yàn)樵摲椒ㄟ€有其他一些牛逼的功能。比如說,如果你想把分隔符包裹在拆分后的字符串的第一部分,可以這樣做:
String cmower = "沉默王二,一枚有趣的程序員";
if (cmower.contains(",")) {
String [] parts = cmower.split("(?<=,)");
System.out.println("第一部分:" + parts[0] +" 第二部分:" + parts[1]);
}
程序輸出的結(jié)果如下所示:
第一部分:沉默王二, 第二部分:一枚有趣的程序員
可以看到分隔符“,”包裹在了第一部分,如果希望包裹在第二部分,可以這樣做:
String [] parts = cmower.split("(?=,)");
“?<= 和 ?= 是什么東東啊?”三妹好奇地問。
“它其實(shí)是正則表達(dá)式中的斷言模式。”我說,“你有時(shí)間的話,可以看看前面我推薦的兩份開源文檔。”

“split() 方法可以傳遞 2 個(gè)參數(shù),第一個(gè)為分隔符,第二個(gè)為拆分的字符串個(gè)數(shù)。”我說。
String cmower = "沉默王二,一枚有趣的程序員,寵愛他";
if (cmower.contains(",")) {
String [] parts = cmower.split(",", 2);
System.out.println("第一部分:" + parts[0] +" 第二部分:" + parts[1]);
}
進(jìn)入 debug 模式的話,可以看到以下內(nèi)容:

也就是說,傳遞 2 個(gè)參數(shù)的時(shí)候,會(huì)直接調(diào)用 substring() 進(jìn)行截取,第二個(gè)分隔符后的就不再拆分了。
來看一下程序輸出的結(jié)果:
第一部分:沉默王二 第二部分:一枚有趣的程序員,寵愛他
“沒想到啊,這個(gè)字符串拆分還挺講究的呀!”三妹感慨地說。
“是的,其實(shí)字符串拆分在實(shí)際的工作當(dāng)中還是挺經(jīng)常用的。前端經(jīng)常會(huì)按照規(guī)則傳遞一長(zhǎng)串字符序列到后端,后端就需要按照規(guī)則把字符串拆分再做處理。”我說。
“嗯,我把今天的內(nèi)容溫習(xí)下,二哥,你休息會(huì)。”三妹說。
---未完待續(xù),期待下集---
點(diǎn)擊「閱讀原文」可直達(dá)《教妹學(xué)Java》專欄的在線閱讀地址!
