Java正則速成秘籍(一)之招式篇


導讀

正則表達式是什么?有什么用?
正則表達式(Regular Expression)是一種文本規則,可以用來校驗查找替換與規則匹配的文本。

又愛又恨的正則

正則表達式是一個強大的文本匹配工具,但是它的規則實在很繁瑣,而且理解起來也頗為蛋疼,容易讓人望而生畏。

如何學習正則

剛接觸正則時,我看了一堆正則的語義說明,但是仍然不明所以。后來,我多接觸一些正則的應用實例,漸漸有了感覺,再結合語義說明,終有領悟。我覺得正則表達式和武俠修練武功差不多,應該先練招式,再練心法。如果一開始就直接看正則的規則,保證你會懵逼。
當你熟悉基本招式(正則基本使用案例)后,也該修煉修煉心法(正則語法)了。真正的高手不能只靠死記硬背那么幾招把式。就像張三豐教張無忌太極拳一樣,領悟心法,融會貫通,少俠你就可以無招勝有招,成為傳說中的絕世高手。

以上閑話可歸納為一句:學習正則應該從實例去理解規則。

欲練神功,必先自宮
打開秘籍:欲練神功,必先自宮!沒有蛋,也就不會蛋疼了。

Java正則速成秘籍分三篇:

展示Java對於正則表達式的支持。

介紹正則表達式的語法規則。

從實戰出發,介紹正則的常用案例。

本文是Java正則速成秘籍的招式篇。主要介紹JDK對於正則表達式的支持。

概述

JDK中的java.util.regex包提供了對正則表達式的支持。

java.util.regex有三個核心類:

  • Pattern類:Pattern是一個正則表達式的編譯表示。
  • Matcher類:Matcher是對輸入字符串進行解釋和匹配操作的引擎。
  • PatternSyntaxException:PatternSyntaxException是一個非強制異常類,它表示一個正則表達式模式中的語法錯誤。

注:需要格外注意一點,在Java中使用反斜杠"\"時必須寫成 "\\"。所以本文的代碼出現形如String regex = "\\$\\{.*?\\}" 其實就是"\$\{.*?\}",不要以為是畫風不對哦。

Pattern類

Pattern類沒有公共構造方法。要創建一個Pattern對象,你必須首先調用其靜態方法compile,加載正則規則字符串,然后返回一個Pattern對象。

Pattern類一樣,Matcher類也沒有公共構造方法。你需要調用Pattern對象的matcher方法來獲得一個Matcher對象。

案例:Pattern和Matcher的初始化

Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);

Matcher類

Matcher類可以說是java.util.regex核心類中的必殺技!

Matcher類有三板斧(三類功能):

  • 校驗
  • 查找
  • 替換

下面我們來領略一下這三塊的功能。

校驗文本是否與正則規則匹配

為了檢查文本是否與正則規則匹配,Matcher提供了以下幾個返回值為boolean的方法。

序號 方法及說明
1 **public boolean lookingAt() ** 嘗試將從區域開頭開始的輸入序列與該模式匹配。
2 **public boolean find() **嘗試查找與該模式匹配的輸入序列的下一個子序列。
3 public boolean find(int start)重置此匹配器,然后嘗試查找匹配該模式、從指定索引開始的輸入序列的下一個子序列。
4 **public boolean matches() **嘗試將整個區域與模式匹配。

如果你傻傻分不清上面的查找方法有什么區別,那么下面一個例子就可以讓你秒懂。

案例:lookingAt vs find vs matches

public static void main(String[] args) {
	checkLookingAt("hello", "helloworld");
	checkLookingAt("world", "helloworld");

	checkFind("hello", "helloworld");
	checkFind("world", "helloworld");

	checkMatches("hello", "helloworld");
	checkMatches("world", "helloworld");
	checkMatches("helloworld", "helloworld");
}

private static void checkLookingAt(String regex, String content) {
	Pattern p = Pattern.compile(regex);
	Matcher m = p.matcher(content);
	if (m.lookingAt()) {
		System.out.println(content + "\tlookingAt: " + regex);
	} else {
		System.out.println(content + "\tnot lookingAt: " + regex);
	}
}

private static void checkFind(String regex, String content) {
	Pattern p = Pattern.compile(regex);
	Matcher m = p.matcher(content);
	if (m.find()) {
		System.out.println(content + "\tfind: " + regex);
	} else {
		System.out.println(content + "\tnot find: " + regex);
	}
}

private static void checkMatches(String regex, String content) {
	Pattern p = Pattern.compile(regex);
	Matcher m = p.matcher(content);
	if (m.matches()) {
		System.out.println(content + "\tmatches: " + regex);
	} else {
		System.out.println(content + "\tnot matches: " + regex);
	}
}

輸出

helloworld	lookingAt: hello
helloworld	not lookingAt: world
helloworld	find: hello
helloworld	find: world
helloworld	not matches: hello
helloworld	not matches: world
helloworld	matches: helloworld

說明

regex = “world” 表示的正則規則是以world開頭的字符串,regex = “hello” 和regex = “helloworld” 也是同理。

  • lookingAt方法從頭部開始,檢查content字符串是否有子字符串於正則規則匹配。
  • find方法檢查content字符串是否有子字符串於正則規則匹配,不管字符串所在位置。
  • matches方法檢查content字符串整體是否與正則規則匹配。

查找匹配正則規則的文本位置

為了查找文本匹配正則規則的位置,Matcher提供了以下方法:

序號 方法及說明
1 **public int start() **返回以前匹配的初始索引。
2 public int start(int group) 返回在以前的匹配操作期間,由給定組所捕獲的子序列的初始索引
3 public int end()返回最后匹配字符之后的偏移量。
4 public int end(int group)返回在以前的匹配操作期間,由給定組所捕獲子序列的最后字符之后的偏移量。
5 public String group()返回前一個符合匹配條件的子序列。
6 public String group(int group)返回指定的符合匹配條件的子序列。

案例:使用start()、end()、group() 查找所有匹配正則條件的子序列

public static void main(String[] args) {
	final String regex = "world";
	final String content = "helloworld helloworld";
	Pattern p = Pattern.compile(regex);
	Matcher m = p.matcher(content);
	System.out.println("content: " + content);

	int i = 0;
	while (m.find()) {
		i++;
		System.out.println("[" + i + "th] found");
		System.out.print("start: " + m.start() + ", ");
		System.out.print("end: " + m.end() + ", ");
		System.out.print("group: " + m.group() + "\n");
	}
}

輸出

content: helloworld helloworld
[1th] found
start: 5, end: 10, group: world
[2th] found
start: 16, end: 21, group: world

說明

例子很直白,不言自明了吧。

替換匹配正則規則的文本

替換方法是替換輸入字符串里文本的方法:

序號 方法及說明
1 public Matcher appendReplacement(StringBuffer sb, String replacement)實現非終端添加和替換步驟。
2 public StringBuffer appendTail(StringBuffer sb)實現終端添加和替換步驟。
3 **public String replaceAll(String replacement) ** 替換模式與給定替換字符串相匹配的輸入序列的每個子序列。
4 public String replaceFirst(String replacement) 替換模式與給定替換字符串匹配的輸入序列的第一個子序列。
5 public static String quoteReplacement(String s)返回指定字符串的字面替換字符串。這個方法返回一個字符串,就像傳遞給Matcher類的appendReplacement 方法一個字面字符串一樣工作。

案例:replaceFirst vs replaceAll

public static void main(String[] args) {
	String regex = "can";
	String replace = "can not";
	String content = "I can because I think I can.";

	Pattern p = Pattern.compile(regex);
	Matcher m = p.matcher(content);

	System.out.println("content: " + content);
	System.out.println("replaceFirst: " + m.replaceFirst(replace));
	System.out.println("replaceAll: " + m.replaceAll(replace));
}

輸出

content: I can because I think I can.
replaceFirst: I can not because I think I can.
replaceAll: I can not because I think I can not.

說明

replaceFirst:替換第一個匹配正則規則的子序列。

replaceAll:替換所有匹配正則規則的子序列。

案例:appendReplacement、appendTail和replaceAll

public static void main(String[] args) {
	String regex = "can";
	String replace = "can not";
	String content = "I can because I think I can.";
	StringBuffer sb = new StringBuffer();
	StringBuffer sb2 = new StringBuffer();

	System.out.println("content: " + content);
	Pattern p = Pattern.compile(regex);
	Matcher m = p.matcher(content);
	while (m.find()) {
		m.appendReplacement(sb, replace);
	}
	System.out.println("appendReplacement: " + sb);
	m.appendTail(sb);
	System.out.println("appendTail: " + sb);
}

輸出

content: I can because I think I can.
appendReplacement: I can not because I think I can not
appendTail: I can not because I think I can not.

說明

從輸出結果可以看出,appendReplacementappendTail方法組合起來用,功能和replaceAll是一樣的。

如果你查看replaceAll的源碼,會發現其內部就是使用appendReplacementappendTail方法組合來實現的。

案例:quoteReplacement和replaceAll,解決特殊字符替換問題

public static void main(String[] args) {
	String regex = "\\$\\{.*?\\}";
	String replace = "${product}";
	String content = "product is ${productName}.";

	Pattern p = Pattern.compile(regex);
	Matcher m = p.matcher(content);
	String replaceAll = m.replaceAll(replace);

	System.out.println("content: " + content);
	System.out.println("replaceAll: " + replaceAll);
}

輸出

Exception in thread "main" java.lang.IllegalArgumentException: No group with name {product}
	at java.util.regex.Matcher.appendReplacement(Matcher.java:849)
	at java.util.regex.Matcher.replaceAll(Matcher.java:955)
	at org.zp.notes.javase.regex.RegexDemo.wrongMethod(RegexDemo.java:42)
	at org.zp.notes.javase.regex.RegexDemo.main(RegexDemo.java:18)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

說明

String regex = "\\$\\{.*?\\}";表示匹配類似${name}這樣的字符串。由於${}都是特殊字符,需要用反義字符\來修飾才能被當做一個字符串字符來處理。

上面的例子是想將 ${productName} 替換為 ${product} ,然而replaceAll方法卻將傳入的字符串中的$當做特殊字符來處理了。結果產生異常。

如何解決這個問題?

JDK1.5引入了quoteReplacement方法。它可以用來轉換特殊字符。其實源碼非常簡單,就是判斷字符串中如果有\$,就為它加一個轉義字符\

我們對上面的代碼略作調整:

m.replaceAll(replace)改為m.replaceAll(Matcher.quoteReplacement(replace)),新代碼如下:

public static void main(String[] args) {
	String regex = "\\$\\{.*?\\}";
	String replace = "${product}";
	String content = "product is ${productName}.";

	Pattern p = Pattern.compile(regex);
	Matcher m = p.matcher(content);
	String replaceAll = m.replaceAll(Matcher.quoteReplacement(replace));

	System.out.println("content: " + content);
	System.out.println("replaceAll: " + replaceAll);
}

輸出

content: product is ${productName}.
replaceAll: product is ${product}.

說明

字符串中如果有\$,不能被正常解析的問題解決。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM