<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在日常的 Java 開發中,由於 JDK 未能提供足夠的常用的操作類庫,通常我們會引入 Apache Commons Lang 工具庫或者 Google Guava 工具庫簡化開發過程。兩個類庫都為 java.lang
API 提供了很多實用工具,比如經常使用的字串操作,基本數值操作、時間操作、物件反射以及並行操作等。
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency>
但是,最近在使用 Apache Commons Lang 工具庫時踩了一個坑,導致程式出現了意料之外的結果。
也是因為踩了這個坑,索性寫下一篇文章好好介紹下 Apache Commons Lang 工具庫中字串操作相關 API。
先說坑是什麼,我們都知道 String 類中到的 split
方法可以分割字串,比如字串 aabbccdd
根據 bc
分割的結果應該是 aab
和 cdd
才對,這樣的結果也很容易驗證。
String str = "aabbccdd"; for (String s : str.split("bc")) { System.out.println(s); } // 結果 aab cdd
可能是因為 String 類中的 split
方法的影響,我一直以為 StringUtils.split
的效果應該相同,但其實完全不同,可以試著分析下面的三個方法輸出結果是什麼,StringUtils 是 Commons Lang 類庫中的字串工具類。
public static void testA() { String str = "aabbccdd"; String[] resultArray = StringUtils.split(str, "bc"); for (String s : resultArray) { System.out.println(s); } }
我對上面 testA 方法的預期是 aab
和 cdd
,但是實際上這個方法的執行結果是:
// testA 輸出
aa
dd
可以看到 b
和 c
字母都不見了,只剩下了 a
和 b
,這是已經發現問題了,檢視原始碼後發現 StringUtils.split
方法其實是按字元進行操作的,不會把分割字串作為一個整體來看,返回的結果中不也會包含用於分割的字元。
驗證程式碼:
public static void testB() { String str = "abc"; String[] resultArray = StringUtils.split(str, "ac"); for (String s : resultArray) { System.out.println(s); } } // testB 輸出 b public static void testC() { String str = "abcd"; String[] resultArray = StringUtils.split(str, "ac"); for (String s : resultArray) { System.out.println(s); } } // testC 輸出 b d
輸出結果和預期的一致了。
點開原始碼一眼看下去,發現在方法註釋中就已經進行提示了:返回的字串陣列中不包含分隔符。
The separator is not included in the returned String array. Adjacent separators are treated as one separator. For more control over the split use the StrTokenizer class....
繼續追蹤原始碼,可以看到最終 split 分割字串時入參有四個。
private static String[] splitWorker( final String str, // 原字串 final String separatorChars, // 分隔符 final int max, // 分割後返回前多少個結果,-1 為所有 final boolean preserveAllTokens // 暫不關注 ) { }
根據分隔符的不同又分了三種情況。
1. 分隔符為 null
final int len = str.length(); if (len == 0) { return ArrayUtils.EMPTY_STRING_ARRAY; } final List<String> list = new ArrayList<>(); int sizePlus1 = 1; int i = 0; int start = 0; boolean match = false; boolean lastMatch = false; if (separatorChars == null) { // Null separator means use whitespace while (i < len) { if (Character.isWhitespace(str.charAt(i))) { if (match || preserveAllTokens) { lastMatch = true; if (sizePlus1++ == max) { i = len; lastMatch = false; } list.add(str.substring(start, i)); match = false; } start = ++i; continue; } lastMatch = false; match = true; i++; } } // ... if (match || preserveAllTokens && lastMatch) { list.add(str.substring(start, i)); }
可以看到如果分隔符為 null
,是按照空白字元 Character.isWhitespace()
分割字串的。分割的演演算法邏輯為:
a. 用於擷取的開始下標置為 0 ,逐字元讀取字串。
b. 碰到分割的目標字元,把擷取的開始下標到當前字元之前的字串擷取出來。
c. 然後用於擷取的開始下標置為下一個字元,等到下一次使用。
d. 繼續逐字元讀取字串、
2. 分隔符為單個字元
邏輯同上,只是判斷邏輯 Character.isWhitespace()
變為了指定字元判斷。
// Optimise 1 character case final char sep = separatorChars.charAt(0); while (i < len) { if (str.charAt(i) == sep) { // 直接比較 ...
3. 分隔符為字串
總計邏輯同上,只是判斷邏輯變為包含判斷。
// standard case while (i < len) { if (separatorChars.indexOf(str.charAt(i)) >= 0) { // 包含判斷 if (match || preserveAllTokens) {
1. 使用 splitByWholeSeparator
方法。
我們想要的是按整個字串分割,StringUtils 工具類中已經存在具體的實現了,使用 splitByWholeSeparator
方法。
String str = "aabbccdd"; String[] resultArray = StringUtils.splitByWholeSeparator(str, "bc"); for (String s : resultArray) { System.out.println(s); } // 輸出 aab cdd
2. 使用 Google Guava 工具庫
關於 Guava 工具庫的使用,之前也寫過一篇文章,可以參考:Guava - 拯救垃圾程式碼
String str = "aabbccdd"; Iterable<String> iterable = Splitter.on("bc") .omitEmptyStrings() // 忽略空值 .trimResults() // 過濾結果中的空白 .split(str); iterable.forEach(System.out::println); // 輸出 aab cdd
3. JDK String.split 方法
使用 String 中的 split 方法可以實現想要效果。
String str = "aabbccdd"; String[] res = str.split("bc"); for (String re : res) { System.out.println(re); } // 輸出 aab cdd
但是 String 的 split 方法也有一些坑,比如下面的輸出結果。
String str = ",a,,b,"; String[] splitArr = str.split(","); Arrays.stream(splitArr).forEach(System.out::println); // 輸出 a b
開頭的逗號,
前出現了空格,末尾的逗號,
後卻沒有空格。
一如既往,文章中程式碼存放在 Github.com/niumoo/javaNotes.
以上就是java開發使用StringUtils.split避坑詳解的詳細內容,更多關於java開發StringUtils.split避坑的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45