快速入門
教學
工具和語言
範例
參考
書籍評論
Regex 工具
grep
PowerGREP
RegexBuddy
RegexMagic
一般應用程式
EditPad Lite
EditPad Pro
語言和函式庫
Boost
Delphi
GNU (Linux)
Groovy
Java
JavaScript
.NET
PCRE (C/C++)
PCRE2 (C/C++)
Perl
PHP
POSIX
PowerShell
Python
R
Ruby
std::regex
Tcl
VBScript
Visual Basic 6
wxWidgets
XML Schema
Xojo
XQuery 和 XPath
XRegExp
資料庫
MySQL
Oracle
PostgreSQL
本網站的更多資訊
簡介
正規表示式快速入門
正規表示式教學
替換字串教學
應用程式和語言
正規表示式範例
正規表示式參考
替換字串參考
書籍評論
可列印 PDF
關於本網站
RSS Feed 和部落格
RegexBuddy—The best regex editor and tester for Java developers!

在 Java 中使用正規表示式

Java 4 (JDK 1.4) 及更新版本透過標準 java.util.regex 套件,全面支援正規表示式。由於 Java 長期以來缺乏正規表示式套件,因此也有許多第 3 方正規表示式套件可供 Java 使用。我將只討論現在是 JDK 一部分的 Sun 正規表示式函式庫。它的品質極佳,優於大多數第 3 方套件。除非您需要支援 JDK 的舊版本,否則 java.util.regex 套件是您的最佳選擇。

Java 5 修復了一些錯誤,並新增支援 Unicode 區塊。Java 6 修復了更多錯誤,但未新增任何功能。Java 7 新增 命名擷取Unicode 腳本。Java 13 允許在 回溯 中使用無限量詞。

String 類別的快速正規表示式方法

Java String 類別有幾個方法,讓您可以在最少的程式碼中使用正規表示式對該字串執行作業。缺點是您無法指定選項,例如「不區分大小寫」或「點號符合換行符號」。基於效能考量,如果您會經常使用相同的正規表示式,也不應使用這些方法。

myString.matches("regex") 會根據字串是否能完全符合正規表示式而傳回 true 或 false。請務必記住,只有當整個字串都能符合時,String.matches() 才會傳回 true。換句話說:「regex」的應用方式就像你寫了「^regex$」,並加上 字串開頭和結尾錨點。這與大多數其他正規表示式函式庫不同,在那些函式庫中,「快速比對測試」方法會在字串中任何位置都能比對到正規表示式時傳回 true。如果 myString 是 abc,則 myString.matches("bc") 會傳回 false。 bc 會比對到 abc,但 ^bc$(實際上是在這裡使用)則不會。

myString.replaceAll("regex", "replacement") 會將字串中所有符合正規表示式的比對結果替換為你指定的替換字串。這沒有什麼意外。字串中所有符合正規表示式的部分都會被替換。你可以透過 $1、$2、$3 等方式在替換文字中使用 擷取括號 的內容。$0(零美元)會插入整個正規表示式比對結果。如果存在第 12 個反向參照,$12 會被替換為第 12 個反向參照;如果反向參照少於 12 個,則會被替換為第 1 個反向參照後接字面值「2」。如果反向參照有 12 個或更多,則無法在替換文字中插入第 1 個反向參照後緊接字面值「2」。

在替換文字中,如果美元符號後面沒有數字,會擲出 IllegalArgumentException。如果反向參照少於 9 個,則美元符號後接大於反向參照數目的數字會擲出 IndexOutOfBoundsException。因此,如果替換字串是由使用者指定的字串,請務必小心。若要插入美元符號作為字面值文字,請在替換文字中使用 \$。當在原始碼中將替換文字編碼為字面值字串時,請記住反斜線本身也必須加上跳脫字元:"\\$"

myString.split("regex") 會在每個正規表示式比對結果處拆分字串。此方法會傳回一個字串陣列,其中每個元素都是兩個正規表示式比對結果之間的原始字串的一部分。比對結果本身不會包含在陣列中。使用 myString.split("regex", n) 來取得一個包含最多 n 個項目的陣列。結果是字串最多會被拆分 n-1 次。字串中的最後一個項目是原始字串未拆分的剩餘部分。

使用 Pattern 類別

在 Java 中,您可以使用 Pattern.compile() 類別工廠編譯正規表示式。此工廠會傳回 Pattern 類型的物件。例如:Pattern myPattern = Pattern.compile("regex"); 您可以指定某些選項作為第二個參數(選用)。Pattern.compile("regex", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE) 會讓正規表示式對美國 ASCII 字元不分大小寫,讓 與換行符號相符,並讓 字串開頭和結尾錨定 也與內嵌換行符號相符。當使用 Unicode 字串時,如果您想讓正規表示式對所有語言的所有字元不分大小寫,請指定 Pattern.UNICODE_CASE。除非您確定字串只包含美國 ASCII 字元,而且您想提升效能,否則您應該總是指定 Pattern.CANON_EQ 來忽略 Unicode 編碼的差異。

如果您會在原始碼中頻繁使用相同的正規表示式,您應該建立一個 Pattern 物件來提升效能。建立 Pattern 物件也讓您可以將相符選項作為第二個參數傳遞給 Pattern.compile() 類別工廠。如果您使用上述其中一個 String 方法,指定選項的唯一方法是將模式修改器內嵌到正規表示式中。在正規表示式的開頭加上 (?i) 會讓它不分大小寫。(?m) 等於 Pattern.MULTILINE(?s) 等於 Pattern.DOTALL,而 (?u)Pattern.UNICODE_CASE 相同。很不幸地,Pattern.CANON_EQ 沒有等效的內嵌模式修改器。

使用 myPattern.split("subject") 來使用已編譯的正規表示式分割主旨字串。此呼叫的結果與 myString.split("regex") 完全相同。不同的是,前者較快,因為正規表示式已經編譯過。

使用 Matcher 類別

除了分割字串(請參閱前一段落)之外,您需要從 Pattern 物件建立一個 Matcher 物件。Matcher 會執行實際的工作。擁有兩個獨立類別的優點是,您可以從單一 Pattern 物件建立多個 Matcher 物件,並因此同時將正規表示式套用至多個主旨字串。

若要建立 Matcher 物件,只需呼叫 Pattern.matcher(),如下所示:myMatcher = Pattern.matcher("subject")。如果您已經從相同的模式建立一個 Matcher 物件,請呼叫 myMatcher.reset("newsubject"),而不是建立一個新的比對器物件,以減少垃圾和提升效能。無論如何,myMatcher 現在都已準備好執行任務。

要找出主旨字串中正規表示式的第一個比對,請呼叫 myMatcher.find()。要找出下一個比對,請再次呼叫 myMatcher.find()。當 myMatcher.find() 傳回 false,表示沒有進一步的比對時,下一次呼叫 myMatcher.find() 將會再次找出第一個比對。Matcher 會在 find() 失敗時自動重設為字串的開頭。

Matcher 物件會保留上次比對的結果。呼叫其方法 start()end()group() 以取得關於整個正規表示式比對和 擷取括號 之間的比對的詳細資料。這些方法各接受一個整數參數,表示 反向參照 的數字。省略參數以取得關於整個正規表示式比對的資訊。start() 是比對中第一個字元的索引。end() 是比對後第一個字元的索引。兩者都相對於主旨字串的開頭。因此比對的長度為 end() - start()group() 傳回由正規表示式或一對擷取括號比對的字串。

myMatcher.replaceAll("replacement")myString.replaceAll("regex", "replacement") 有完全相同的結果。再次強調,差別在於速度。

Matcher 類別讓您可以在自己的程式碼中執行搜尋和取代,並計算每個正規表示式比對的取代文字。您可以使用 appendReplacement()appendTail() 來執行此操作。方法如下

StringBuffer myStringBuffer = new StringBuffer();
myMatcher = myPattern.matcher("subject");
while (myMatcher.find()) {
  if (checkIfThisMatchShouldBeReplaced()) {
    myMatcher.appendReplacement(myStringBuffer, computeReplacementString());
  }
}
myMatcher.appendTail(myStringBuffer);

顯然地,checkIfThisMatchShouldBeReplaced()computeReplacementString() 是您提供的 placeholder 方法。第一個傳回 true 或 false,表示是否應該進行任何取代。請注意,略過取代比使用與比對完全相同的文字取代比對要快得多。computeReplacementString() 傳回實際的取代字串。

正規表示式、字面字串和反斜線

在 Java 字面字串中,反斜線是跳脫字元。字面字串 "\\" 是單一反斜線。在正規表示式中,反斜線也是跳脫字元。正規表示式 \\ 比對單一反斜線。此正規表示式作為 Java 字串時,會變成 "\\\\"。沒錯:4 個反斜線比對一個反斜線。

正規表示式 \w 比對一個字元字元。作為 Java 字串時,這會寫成 "\\w"

當在 Java 程式碼中提供字面 Java 字串作為 String.replaceAll() 等方法的取代字串時,也會發生相同的反斜線混亂。在取代文字中,當您要使用實際的美元符號或反斜線取代正規表示式比對時,美元符號必須編碼為 \$,反斜線必須編碼為 \\。不過,反斜線也必須在字面 Java 字串中跳脫。因此,當寫成字面 Java 字串時,取代文字中的單一美元符號會變成 "\\$"。單一反斜線會變成 "\\\\"。沒錯:4 個反斜線插入一個反斜線。

進一步閱讀

Java 正則表示式 - 馴服 java.util.regex 引擎如果您想要更詳細了解 java.util.regex 套件所提供的功能,您可以取得由 Mehran Habibi 編寫、Apress 出版的「Java 正則表示式」。雖然本網站上的正則表示式教學更完整、更詳細,但 Habibi 先生的書包含更循序漸進的正則表示式教學。這本書絕對提供比這個網頁更多的資訊和範例,說明 java.util.regex 套件。

我對 Java 正則表示式這本書的評論

| 快速開始 | 教學 | 工具和語言 | 範例 | 參考 | 書籍評論 |

| grep | PowerGREP | RegexBuddy | RegexMagic |

| EditPad Lite | EditPad Pro |

| Boost | Delphi | GNU (Linux) | Groovy | Java | JavaScript | .NET | PCRE (C/C++) | PCRE2 (C/C++) | Perl | PHP | POSIX | PowerShell | Python | R | Ruby | std::regex | Tcl | VBScript | Visual Basic 6 | wxWidgets | XML Schema | Xojo | XQuery & XPath | XRegExp |

| MySQL | Oracle | PostgreSQL |