1. 程式人生 > >Java開發筆記(三十七)利用正則串分割字串

Java開發筆記(三十七)利用正則串分割字串

前面介紹了處理字串的常用方法,還有一種分割字串的場景也很常見,也就是按照某個規則將字串切割為若干子串。分割規則通常是指定某個分隔符,根據字串內部的分隔符將字串進行分割,例如逗號、空格等等都可以作為字串的分隔符。正好String型別提供了split方法用於切割字串,只要字串變數呼叫split方法,並把分隔符作為輸入引數,該方法即可返回分割好的字串陣列。
下面的split呼叫程式碼例子演示瞭如何按照逗號和空格切割字串:

	// 通過逗號分割字串
	private static void splitByComma() {
		String commaStr = "123,456,789";
		// 利用split方法指定按照逗號切割字串
		String[] commaArray = commaStr.split(",");
		for (String item : commaArray) {
			System.out.println("comma item = "+item);
		}
	}

	// 通過空格分割字串
	private static void splitBySpace() {
		String spaceStr = "123 456 789";
		// 利用split方法指定按照空格切割字串
		String[] spaceArray = spaceStr.split(" ");
		for (String item : spaceArray) {
			System.out.println("space item = "+item);
		}
	}

除了逗號和空格以外,點號和豎線也常常用來分隔字串,但是對於點號和豎線,split方法的呼叫程式碼不會得到預期的結果。相反,split(".")無法得到分割後的字串陣列,也就是說結果的字串陣列為空;而split("|")分割得到的字串陣列,每個陣列元素只有一個字元,其結果類似於toCharArray。究其原因,緣於split方法的輸入引數理應是個正則串,並非普通的分隔字元。由於點號和豎線都是正則串的保留字元,因此無法直接在正則串中填寫,必須進行轉義處理方可。如同回車符和換行符在普通字串中通過字首的反斜杆轉義那樣(回車符對應\r,換行符對應\n),正則字串通過在原字元前面新增兩個反斜杆來轉義,像點號字元在正則串中對應的轉義符為“\\.”,而豎線在正則串中對應的轉義符為“\\|”。經過轉移處理之後,通過點號和豎線切割字串的正確程式碼寫法如下所示:

	// 通過點號分割字串
	private static void splitByDot() {
		String dotStr = "123.456.789";
		// split(".")無法得到分割後的字串陣列
		//String[] dotArray = dotStr.split(".");
		// 點號是正則串的保留字元,需要進行轉義(在點號前面加兩個反斜杆)
		String[] dotArray = dotStr.split("\\.");
		for (String item : dotArray) {
			System.out.println("dot item = "+item);
		}
	}

	// 通過豎線分割字串
	private static void splitByLine() {
		String lineStr = "123|456|789";
		// split("|")分割得到的字串陣列,每個陣列元素只有一個字元,類似於toCharArray的結果
		//String[] lineArray = lineStr.split("|");
		// 豎線是正則串的保留字元,需要進行轉義(在豎線前面加兩個反斜杆)
		String[] lineArray = lineStr.split("\\|");
		for (String item : lineArray) {
			System.out.println("line item = "+item);
		}
	}

豎線符號之所以被定為正則串的保留字元,是因為它在正則表示式裡起到了“或”的判斷作用,例如正則串“,| ”表示逗號和空格都是滿足條件的分隔符;一個字串如果同時包含一個逗號和一個空格,那麼按照“,| ”切割的結果將是長度為3的字串陣列;也就是說,原始串被逗號分割一次後又被空格分割一次,這樣一共分割兩次最終得到了三個子串。下面的程式碼演示了使用正則串“,| ”切割字串的效果:

	// 利用豎線同時指定多個串來分割字串
	private static void splitByMixture() {
		String mixtureStr = "123,456 789";
		// 正則串裡的豎線表示“或”,豎線左邊和右邊的字元都可以用來分割字串
		String[] mixtureArray = mixtureStr.split(",| ");
		for (String item : mixtureArray) {
			System.out.println("mixture item = "+item);
		}
	}

 

當然,正則串中的保留字元不僅包括點號和豎線,還包括好些常見的符號,比如加號(+)、星號(*)、橫線(-),在正則串中均需進行轉義。其中加號的正則轉義符為“\\+”,星號的正則轉義符為“\\*”,橫線的正則轉義符為“\\-”。這麼估摸下,加減乘除的四則運算子號,只有除法的斜杆符(/)、取餘數的百分號(%)無需轉義處理。倘若有個字串,要求以四則運算的五個符號進行切割,則需通過豎線把這幾個轉義後的字元加以連線,構成形如“\\+|\\*|\\-|/|%”的正則串。於是按照加減乘除符號切割字串的程式碼就變為下面這樣:

	// 通過算術的加減乘除符號來分割字串
	private static void splitByArith() {
		String arithStr = "123+456*789-123/456%789";
		// 正則串裡的加號、星號、橫線都要轉義,加減乘除符號之間通過豎線隔開
		String[] arithArray = arithStr.split("\\+|\\*|\\-|/|%");
		for (String item : arithArray) {
			System.out.println("arith item = "+item);
		}
	}

 

分割用的正則串,不單單是一個個字元,還支援好幾個字元組成的字串。譬如“(1)”、“(2)”、“(3)”都可以作為分隔串,注意圓括號內部的數字可以是0到9,如此一來,從“(0)”到“(9)”的分隔串合集豈不是要寫成以下這般:“(0)|(1)|(2)|(3)|(4)|(5)|(6)|(7)|(8)|(9)”?然而以上正則串的寫法有兩個錯誤:
1、圓括號也是正則表示式的保留字元,所以不能直接在正則串中書寫“(”和“)”,而必須寫成轉義形式“\\(”和”“\\)”。
2、作為保留字元的圓括號,其作用類似數值計算之時的圓括號,都是通過圓括號把括號內外的運算區分開。而豎線符號“|”的或運算優先順序不如圓括號,因此每逢複雜一點的或運算,應當把圓括號放在整個邏輯運算式子的外面。
綜合以上兩點,修正之後的正則串應該改成下列形式:“\\((0|1|2|3|4|5|6|7|8|9)\\)”。可是該式子的豎線太多,意思僅僅是獲取0到9之間的某個數字之一,針對這種情況,正則表示式引入了另外一種簡化的寫法,即通過方括號包裹0123456789,形如“\\([0123456789]\\)”,同樣指代0到9之間的某個數字,從而省略了若干個豎線。進一步說,日常生活中0到9,常常寫做“0-9”,於是對應更簡單的正則串“\\([0-9]\\)”。
其實0到9正好涵蓋了所有的一位數字,對於一位數字而言,正則表示式提供了專門的表示式“\\d{1}”。式子前面的“\\d”代表某個數字,式子後面的“{1}”代表字元數量是1位。推而廣之,“\\d{2}”表示兩位數字,“\\d{3}”表示三位數字,等等。像這個正則例子只有一位數字,甚至尾巴的“{1}”都可以去掉,因為“\\d”預設就是一位數字。
一口氣介紹了許多種從0到9的正則表達串,接下來不妨逐一驗證這些正則串是否有效,驗證用的程式碼例子如下所示:

	// 通過圓括號及其內部數字來分割字串
	private static void splitByBracket() {
		String bracketStr = "(1)123;(2)456;(3)789;";
		// 圓括號也是正則串的保留字元,0到9這九個數字使用豎線隔開
		//String[] bracketArray = bracketStr.split("\\((0|1|2|3|4|5|6|7|8|9)\\)");
		// 利用方括號聚集一群字元,表示這些字元之間是“或”的關係,故而可省略豎線
		//String[] bracketArray = bracketStr.split("\\([0123456789]\\)");
		// 連續的數字可使用橫線連線首尾數字,例如“0-9”表示從0到9之間的所有數字
		//String[] bracketArray = bracketStr.split("\\([0-9]\\)");
		// 利用“\\d”即可表達0到9的數字,後面的{1}表示1位數字,依此類推{3}表示三位數字
		//String[] bracketArray = bracketStr.split("\\(\\d{1}\\)");
		// “\\d”預設就是1位數字,此時後面的{1}可直接略去
		String[] bracketArray = bracketStr.split("\\(\\d\\)");
		for (String item : bracketArray) {
			System.out.println("bracket item = "+item);
		}
	}

 

上述的幾種正則串,只能表達從“(0)”到“(9)”的分隔串,然而圓括號內部還可能是兩位數字或者三位數字,比如“(10)”、“(12)”、“(001)”這樣。對於數字位數不固定的情況,可以把“\\d”改為“\\d+”,末尾多出來的加號,表示前面的字元允許有一位,也允許有多位。此時正則串添加了加號的字串切割程式碼見下:

	// 通過特殊符號的加號來分割字串
	private static void splitWithPlus() {
		String bracketStr = "(1)123;(2)456;(13)789;";
		// 正則串裡的加號表示可以有1到多個前面的字元
		String[] bracketArray = bracketStr.split("\\(\\d+\\)");
		for (String item : bracketArray) {
			System.out.println("plus item = "+item);
		}
	}

 

上面說的位數不固定,畢竟至少還有一位。假設現在某個字元不但位數不確定,甚至還可能沒有該字元(位數為0),採用寫法“\\d+”就無法奏效了。要想滿足位數可有可無的情況,需將末尾的加號換成星號,也就是改成“\\d*”,此時改用星號的字串切割程式碼變為下面這般:

	// 通過特殊符號的星號來分割字串
	private static void splitWithStar() {
		String bracketStr = "()123;(2)456;(13)789;";
		// 正則串裡的星號表示可以有0到多個前面的字元
		String[] bracketArray = bracketStr.split("\\(\\d*\\)");
		for (String item : bracketArray) {
			System.out.println("star item = "+item);
		}
	}

 

到目前位置,分隔符還僅限於標點和數字,如果引入英文字母作為分隔串,又該如何書寫呢?注意英文字母區分大小寫,因而使用“a-z”表示所有的小寫字母,使用“A-Z”表示所有的大寫字母。如果採納“(a)”、“(B)”、“(c)”這種大小寫混合的分隔串,就得通過正則串“\\([a-zA-Z]\\)”來表達,對應的字串切割程式碼如下所示:

	// 通過大小寫字母來分割字串
	private static void splitWithLetter() {
		String bracketStr = "(a)123;(B)456;(c)789;";
		// 在正則串中表達小寫字母和大寫字母
		String[] bracketArray = bracketStr.split("\\([a-zA-Z]\\)");
		for (String item : bracketArray) {
			System.out.println("letter item = "+item);
		}
	}

 

現在有個麻煩的業務場景,圓括號內部不但可能是數字和字母,還可能是其它標點符號,這下難不成把眾多標點符號一個個羅列出來?要知道標點符號可沒有“0-9”、“a-z”、“A-Z”的簡單寫法噢。不過這難不倒強大的正則表示式,因為點號作為正則的保留字元,它代表了除回車\r和換行\n以外的其它字元,所以使用“\\(.\\)”即可表達符合要求的任意字元了,當然是被圓括號包裹著的、除了回車\r和換行\n以外的任意字元。下面便是匹配前述場景的字串切割程式碼例子:

	// 通過特殊符號的點號來分割字串
	private static void splitWithDot() {
		String bracketStr = "(1)123;(B)456;(%)789;";
		// 正則串裡的點號表示除了回車\r和換行\n以外的其它字元
		String[] bracketArray = bracketStr.split("\\(.\\)");
		for (String item : bracketArray) {
			System.out.println("dot item = "+item);
		}
	}

  

更多Java技術文章參見《Java開發筆記(序)章節目錄