【Java實用技術】字串的拆分用什麼方法好?有一半程式設計師都掉過split坑

語言: CN / TW / HK

highlight: vs2015 theme: cyanosis


必備字串操作

我們將字串操作分為下面6種:

  1. 基本操作方法
  2. 字串判空、比較
  3. 字串擷取和拆分
  4. 字串查詢和替換
  5. 字串和其他型別資料的轉換
  6. 字串拼接和格式化

今天我們來講解第3.2節。

字串拆分原生方法

對於字串拆分的原生操作

java //字串拆分原生方法: public String[] String.split(String regex)

問題: 對字串中含有“|”分割符部分進行拆分,怎麼寫?

直接str.split("|")可以嗎?當然可以,但是結果是不是你想要的呢?

java String str = "a.|b."; *** 原生 String.split 方法結果 ***** [a, ., |, b, .]

答案: 如果只是單純想按照“|”分割,正確的寫法是ss.split("\\|"),因為這裡碰到煩人的正則表示式。

java String str = "a.|b."; // 使用“|”拆分 System.out.println("*** 原生 String.split 方法 *****"); if (StringUtils.isNotEmpty(str)) {     String[] s1 = str.split("\\|");     System.out.println(Arrays.toString(s1)); }

split原始碼分析

```java // 選取原始碼中關鍵的分析

/* 圍繞給定正則表示式的匹配拆分此字串。 此方法的工作方式就像通過使用給定表示式和零限制引數呼叫雙引數split方法一樣。 因此,結果陣列中不包含尾隨空字串。 引數:regex – 定界正則表示式 返回:通過圍繞給定正則表示式的匹配拆分此字串計算出的字串陣列 丟擲:PatternSyntaxException – 如果正則表示式的語法無效 / public String[] split(String regex) {     return split(regex, 0); }

/ 圍繞給定正則表示式的匹配拆分此字串。 以str形式呼叫此方法。 split(regex , n)產生與表示式相同的結果: Pattern.compile(regex).split(str , n) / public String[] split(String regex, int limit) {         / 下面情況使用快速模式:          (1)單個字元但是不能是正則中的元字元,比如".$|()[{^?+\"          (2)兩個字元,第一個字元是\,第二個字元不能是數字或者字母[0-9a-zA-Z]          /         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))         {             //省略 使用substring拆分  過程             String[] result = new String[resultSize];             return list.subList(0, resultSize).toArray(result);         }        // 其他場景使用正則表示式拆分         return Pattern.compile(regex).split(this, limit);     } ```

原始碼不復雜,核心內容是:

(1)不是正則表示式的元字元".$|()[{^?*+\"`開頭但後面**不是**[0-9a-zA-Z]`的使用快速拆分模式

(2)其他情況使用正則表示式匹配模式拆分

這裡涉及到另外一個知識點,正則表示式,如果對正則表示式感興趣的可以參考其他博文。

當然我認為你是沒有興趣的,而且很多程式設計師也很討厭正則表達的規則,需要用的時候再翻閱。

對於(1),如果你十分精通正則表示式,可以很快知道哪些是正則元字元,你可以嘗試使用原生的方法,畢竟使用方便且拆分速度確實快。

對於(2),因為正則表示式需要編譯再匹配,比較耗時,所以不建議使用正則表示式。至於為什麼正則表示式會慢,你就想你只見過一個姑娘一面就想找到她,和你知道她的名字地址具體資訊比,哪個找到她更快?

綜上所述,原生方法存在以下問題:

  • 使用前要判空
  • 小心正則表示式裡面特殊字元
  • 效能不佳

為什麼不推薦StringTokenizer

當然有同學可能會說jdk還有一個用於拆分字串的類:StringTokenizer,它不是基於正則表示式進行拆分,效能應該高。但是筆者並不推薦使用此方法進行字串拆分。我們先看原始碼:

java /** StringTokenizer is a legacy class that is retained for compatibility reasons although its use is  discouraged in new code. It is recommended that anyone seeking this functionality  use the split method of String or the java.util.regex package instead. Since: JDK1.0 */ public class StringTokenizer implements Enumeration<Object> {     //... }

只看關鍵單詞discouraged=不推薦。知道它是一個為了相容而遺留下的類,官方不推薦使用就足夠的。

另外一個不推薦的原因是:本來很簡單的一個拆分,使用它需要搭配while迴圈,索性就不要用它了。

推薦字串拆分操作

善用StringUtils.splitXX()

推薦使用Apache工具類StringUtils.splitXX()

優勢: 不用擔心字串為null(空),方法名直白。

推薦常用方法:

```java // 按照指定分割符拆分字串 public static String[] split(String str, String separator)  // 按照完整分割符拆分字串 public static String[] splitByWholeSeparator(String str, String separator)  // 拆分字串,保留所有分割符位置 --較少使用 public static String[] splitPreserveAllTokens(String str, String separatorChars)

// 獲取分割符的前部分  public static String[] substringBefore(String str, String separatorChars)  //獲取分割符的後面部分  public static String[] substringAfterLast(String str, String separatorChars) ```

對於上面問題按照“|”拆分,可以一行程式碼搞定。

java String str = "a.|b."; String[] s2 = StringUtils.split(str, "|"); /* 輸出:[a., b.] */

開發中比較常見的需求是對", . | * : "等進行拆分。如果你對正則元字元不熟悉,很容易寫出錯誤的拆分。

所以還是建議直接使用StringUtils工具類進行拆分,減少寫程式碼時的思考成本。

StringUtils.split也有坑

img

還是上述例子,如果對字串"a.|b.c"按照".|"進行拆分,使用StringUtils.split可以嗎?

java // 問題:請使用“.|”拆分  String str = "a.|b.c"; String[] s2 = StringUtils.split(str, ".|"); /* 執行結果:[a, b, c] 並不是期望的[a, b.c] */

答案是不行,拆分的結果是[a, b, c],並不是期望的[a, b.c], 這是因為StringUtils.split方法是將多字元分割符拆成單個分割符進行拆分,再將非分割符部分返回。

如果希望對多字元分割符拆分,請使用StringUtils.splitByWholeSeparator()方法。

這個小細節,希望大家能夠避坑。

demo用例

```java import org.apache.commons.lang3.StringUtils;

import java.util.Arrays; import java.util.StringTokenizer;

/*  * Java實用技術課程 By Pandas.  * 公眾號:Java實用技術手冊  * JDK版本:jdk1.8.0_66  *  * @author Pandas  * @date 2021/10/30  / public class StringSplitDemo {

/      * 字串拆分方法用哪個方法好?      /     public static void main(String[] args) {         // 問題:請使用“|”拆分         String str = "a.|b.c";         System.out.println(" 原生 String.split 方法 *");         if (StringUtils.isNotEmpty(str)) {             String[] s1 = str.split("\|");             System.out.println(Arrays.toString(s1));         }         String[] s11 = StringUtils.split(str, "|");         System.out.println(Arrays.toString(s11));

System.out.println(" 原生 StringTokenizer 方法 **");         StringTokenizer tokenizer = new StringTokenizer(str, "|");         while (tokenizer.hasMoreTokens()) {             System.out.println(tokenizer.nextToken());         }

// "a.|b.c"         str = "a.|b.c";         System.out.println(" StringUtils 方法 **");         String[] s2 = StringUtils.split(str, ".|");         System.out.println(Arrays.toString(s2));

String[] s3 = StringUtils.splitByWholeSeparator(str, ".|");         System.out.println(Arrays.toString(s3));

str = "a.|b...";         String[] s4 = StringUtils.splitPreserveAllTokens(str, ".");         System.out.println(Arrays.toString(s4));     } }

/ 執行結果===>  原生 String.split 方法 * [a., b.c] [a., b.c]  原生 StringTokenizer 方法 * a. b.c  StringUtils 方法 *** [a, b, c] [a, b.c] [a, |b, , , ] / ```

總結

  • 直接使用StringUtils最省事(注意上面寫的避坑)。
  • 如果是對非特殊字元拆分,比如對字母數字拆分,可以使用原生的split方法。
  • 不要使用StringTokenizer。

感謝閱讀本期內容,希望對新入行的你有幫助。

如果你意猶未盡,後面有一個字串拆分姊妹篇,歡迎繼續檢視。

往期內容:

我是Pandas,專注Java程式設計實用技術分享,公眾號Java實用技術手冊和B站均有視訊解說,歡迎來玩。

如果你覺得這篇文章有用,別忘了點贊+關注,一起進步!