Java正則表達式API指南
1. 概述
在本教程中,我們將討論 Java 正則表達式 API,以及如何在 Java 編程語言中使用正則表達式。
在正則表達式的世界中,有許多不同的風格可供選擇,例如 grep、Perl、Python、PHP、awk 等等。
這意味著在一種編程語言中可以工作的正則表達式,可能在另一種語言中無法工作。Java 中的正則表達式語法與 Perl 中的語法最為相似。
2. 設置
在 Java 中使用正則表達式,我們不需要任何特殊設置。JDK 包含了一個專門用于正則表達式操作的包,java.util.regex
,只需在代碼中導入即可。
此外,java.lang.String
類也內置了正則表達式的支持,通常在我們的代碼中使用。
3. Java 正則表達式包
java.util.regex
包由三個類組成:Pattern
、Matcher
和 PatternSyntaxException
:
Pattern
對象是已編譯的正則表達式。Pattern
類沒有公共構造函數。要創建模式,首先必須調用其公共靜態方法compile
,該方法返回一個Pattern
對象。這些方法接受正則表達式作為第一個參數。Matcher
對象解釋模式,并針對輸入字符串執行匹配操作。它也沒有公共構造函數。我們通過在Pattern
對象上調用matcher
方法來獲取Matcher
對象,并傳遞我們想要匹配的文本。PatternSyntaxException
對象是未檢查的異常,表示正則表達式模式中的語法錯誤。
我們將詳細探討這些類,但首先必須了解如何在 Java 中構造正則表達式。
如果我們已經熟悉了其他環境中的正則表達式,可能會發現一些差異,但它們非常小。
4. 簡單示例
讓我們從最簡單的正則表達式使用案例開始。如前所述,當我們將正則表達式應用于字符串時,它可能會匹配零次或多次。
最基本的模式匹配形式是對字符串字面的匹配。例如,如果正則表達式是 foo
且輸入字符串是 foo
,匹配將成功,因為兩個字符串是相同的:
@Test
public void givenText_whenSimpleRegexMatches_thenCorrect() {
Pattern pattern = Pattern.compile("foo");
Matcher matcher = pattern.matcher("foo");
assertTrue(matcher.find());
}
我們首先通過調用其靜態方法 compile
并傳遞要使用的模式,來創建一個 Pattern
對象。
然后我們通過調用 Pattern
對象的 matcher
方法并傳遞要檢查匹配的文本來創建一個 Matcher
對象。
最后,我們調用 Matcher
對象中的 find
方法。
find
方法在輸入文本中逐步前進并為每次匹配返回 true,因此我們可以使用它來計算匹配的次數:
@Test
public void givenText_whenSimpleRegexMatchesTwice_thenCorrect() {
Pattern pattern = Pattern.compile("foo");
Matcher matcher = pattern.matcher("foofoo");
int matches = 0;
while (matcher.find()) {
matches++;
}
assertEquals(matches, 2);
}
由于我們將運行更多測試,可以將找到匹配次數的邏輯抽象為一個名為 runTest
的方法:
public static int runTest(String regex, String text) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
int matches = 0;
while (matcher.find()) {
matches++;
}
return matches;
}
當我們得到 0 次匹配時,測試應該失敗;否則,它應該通過。
5. 元字符
元字符影響模式的匹配方式;它們在一定程度上為搜索模式添加了邏輯。Java API 支持幾個元字符,最簡單的就是點“.”,它匹配任何字符:
@Test
public void givenText_whenMatchesWithDotMetach_thenCorrect() {
int matches = runTest(".", "foo");
assertTrue(matches > 0);
}
讓我們考慮前面的示例,其中正則表達式 foo
匹配了文本 foo
以及 foofoo
,兩次。如果我們在正則表達式中使用點元字符,在第二種情況下我們不會得到兩次匹配:
@Test
public void givenRepeatedText_whenMatchesOnceWithDotMetach_thenCorrect() {
int matches = runTest("foo.", "foofoo");
assertEquals(matches, 1);
}
注意正則表達式中 foo
后面的點。匹配器匹配每個 foo
之后的文本,因為最后的點部分表示任何字符。所以在找到第一個 foo
后,其余部分被視為任意字符。這就是為什么只有一次匹配的原因。
API 還支持其他元字符 <([{\\^-=$!|]})?*+.>
,我們將在本文進一步探討。
6. 字符類
瀏覽官方的 Pattern
類規范時,我們會發現支持的正則表達式結構的摘要。在字符類下,我們有大約 6 種結構。
6.1 與或類
我們將其構造為 [abc]
。它匹配集合中的任意一個元素:
@Test
public void givenORSet_whenMatchesAny_thenCorrect() {
int matches = runTest("[abc]", "b");
assertEquals(matches, 1);
}
如果它們都出現在文本中,將分別匹配每個元素,而不考慮順序:
@Test
public void givenORSet_whenMatchesAnyAndAll_thenCorrect() {
int matches = runTest("[abc]", "cab");
assertEquals(matches, 3);
}
它們也可以作為字符串的一部分進行交替。在下面的示例中,當我們通過將集合中的每個元素與第一個字母交替創建不同的單詞時,所有這些都匹配:
@Test
public void givenORSet_whenMatchesAllCombinations_thenCorrect() {
int matches = runTest("[bcr]at", "bat cat rat");
assertEquals(matches, 3);
}
6.2 NOR類
上述集合通過在第一個元素位置添加插入符進行否定:
@Test
public void givenNORSet_whenMatchesNon_thenCorrect() {
int matches = runTest("[^abc]", "g");
assertTrue(matches > 0);
}
這里是另一個例子:
@Test
public void givenNORSet_whenMatchesAllExceptElements_thenCorrect() {
int matches = runTest("[^bcr]at", "sat mat eat");
assertTrue(matches > 0);
}
6.3 范圍類
我們可以通過使用連字符(-)定義一個匹配文本應落入的范圍。我們也可以對范圍進行否定。
匹配大寫字母:
@Test
public void givenUpperCaseRange_whenMatchesUpperCase_
thenCorrect() {
int matches = runTest(
"[A-Z]", "Two Uppercase alphabets 34 overall");
assertEquals(matches, 2);
}
匹配小寫字母:
@Test
public void givenLowerCaseRange_whenMatchesLowerCase_
thenCorrect() {
int matches = runTest(
"[a-z]", "Two Uppercase alphabets 34 overall");
assertEquals(matches, 26);
}
匹配大寫和小寫字母:
@Test
public void givenBothLowerAndUpperCaseRange_
whenMatchesAllLetters_thenCorrect() {
int matches = runTest(
"[a-zA-Z]", "Two Uppercase alphabets 34 overall");
assertEquals(matches, 28);
}
匹配給定的數字范圍:
@Test
public void givenNumberRange_whenMatchesAccurately_
thenCorrect() {
int matches = runTest(
"[1-5]", "Two Uppercase alphabets 34 overall");
assertEquals(matches, 2);
}
匹配另一個數字范圍:
@Test
public void givenNumberRange_whenMatchesAccurately_
thenCorrect2(){
int matches = runTest(
"3[0-5]", "Two Uppercase alphabets 34 overall");
assertEquals(matches, 1);
}
6.4 聯合類
聯合字符類是通過組合兩個或更多字符類產生的結果:
@Test
public void givenTwoSets_whenMatchesUnion_thenCorrect() {
int matches = runTest("[1-3[7-9]]", "123456789");
assertEquals(matches, 6);
}
上面的測試只會匹配九個整數中的六個,因為聯合集跳過了4、5和6。
6.5 交叉點類
類似于聯合類,這個類是通過從兩個或更多集合中挑選公共元素得到的。要應用交集,我們使用&&:
@Test
public void givenTwoSets_whenMatchesIntersection_thenCorrect() {
int matches = runTest("[1-6&&[3-9]]", "123456789");
assertEquals(matches, 4);
}
我們會得到四個匹配,因為這兩個集合的交集中只有四個元素。
6.6 減法類
我們可以使用減法來否定一個或多個字符類。例如,我們可以匹配一組奇數十進制數:
@Test
public void givenSetWithSubtraction_whenMatchesAccurately_thenCorrect() {
int matches = runTest("[0-9&&[^2468]]", "123456789");
assertEquals(matches, 5);
}
只有1、3、5、7、9會被匹配。
7. 預定義字符類
Java正則表達式API還接受預定義的字符類。上述的一些字符類可以用更簡短的形式表示,雖然這會讓代碼不那么直觀。Java正則表達式的一個特殊方面是轉義字符。
正如我們將看到的,大多數字符將以反斜杠開頭,這在Java中有特殊的含義。要使這些由Pattern類編譯,前導反斜杠必須被轉義,即\\d變為\\\\d。
匹配數字,相當于[0-9]:
@Test
public void givenDigits_whenMatches_thenCorrect() {
int matches = runTest("\\\\d", "123");
assertEquals(matches, 3);
}
匹配非數字,相當于[^0-9]:
@Test
public void givenNonDigits_whenMatches_thenCorrect() {
int mathces = runTest("\\\\D", "a6c");
assertEquals(matches, 2);
}
匹配空格:
@Test
public void givenWhiteSpace_whenMatches_thenCorrect() {
int matches = runTest("\\\\s", "a c");
assertEquals(matches, 1);
}
匹配非空格:
@Test
public void givenNonWhiteSpace_whenMatches_thenCorrect() {
int matches = runTest("\\\\S", "a c");
assertEquals(matches, 2);
}
匹配單詞字符,相當于[a-zA-Z_0-9]:
@Test
public void givenWordCharacter_whenMatches_thenCorrect() {
int matches = runTest("\\\\w", "hi!");
assertEquals(matches, 2);
}
匹配非單詞字符:
@Test
public void givenNonWordCharacter_whenMatches_thenCorrect() {
int matches = runTest("\\\\W", "hi!");
assertEquals(matches, 1);
}
8. 量詞
Java 正則表達式 API 也允許我們使用量詞。這使我們可以通過指定匹配的出現次數來進一步調整匹配的行為。
要匹配文本零次或一次,我們使用?量詞:
@Test
public void givenZeroOrOneQuantifier_whenMatches_thenCorrect() {
int matches = runTest("\\\\a?", "hi");
assertEquals(matches, 3);
}
或者,我們可以使用大括號語法,Java 正則表達式 API 也支持這種語法:
@Test
public void givenZeroOrOneQuantifier_whenMatches_thenCorrect2() {
int matches = runTest("\\\\a{0,1}", "hi");
assertEquals(matches, 3);
}
這個示例引入了零長度匹配的概念。當量詞的匹配閾值為零時,它總是會匹配文本中的所有內容,包括每個輸入末尾的空字符串。這意味著即使輸入為空,它也會返回一個零長度的匹配。
這解釋了為什么我們在上述示例中得到了三次匹配,盡管字符串的長度只有兩個。第三次匹配是零長度的空匹配。
要匹配文本零次或無限次,我們使用*量詞,它類似于?:
@Test
public void givenZeroOrManyQuantifier_whenMatches_thenCorrect() {
int matches = runTest("\\\\a*", "hi");
assertEquals(matches, 3);
}
支持的替代方案:
@Test
public void givenZeroOrManyQuantifier_whenMatches_thenCorrect2() {
int matches = runTest("\\\\a{0,}", "hi");
assertEquals(matches, 3);
}
有區別的量詞是+,它的匹配閾值為1。如果所需的字符串根本沒有出現,則不會有任何匹配,甚至連零長度的字符串也不會匹配:
@Test
public void givenOneOrManyQuantifier_whenMatches_thenCorrect() {
int matches = runTest("\\\\a+", "hi");
assertFalse(matches);
}
支持的替代方案:
@Test
public void givenOneOrManyQuantifier_whenMatches_thenCorrect2() {
int matches = runTest("\\\\a{1,}", "hi");
assertFalse(matches);
}
與 Perl 和其他語言一樣,我們可以使用大括號語法來匹配特定次數的文本:
@Test
public void givenBraceQuantifier_whenMatches_thenCorrect() {
int matches = runTest("a{3}", "aaaaaa");
assertEquals(matches, 2);
}
在上述示例中,我們得到了兩次匹配,因為只有當a連續出現三次時才會產生匹配。然而,在下一個測試中,我們不會得到匹配,因為文本只連續出現了兩次:
@Test
public void givenBraceQuantifier_whenFailsToMatch_thenCorrect() {
int matches = runTest("a{3}", "aa");
assertFalse(matches > 0);
}
當我們在大括號中使用范圍時,匹配將是貪婪的,從范圍的高端開始匹配:
@Test
public void givenBraceQuantifierWithRange_whenMatches_thenCorrect() {
int matches = runTest("a{2,3}", "aaaa");
assertEquals(matches, 1);
}
在這里我們指定了至少兩次出現,但不超過三次,所以我們得到了一次匹配,匹配器看到的是一個aaa和一個孤立的a,它無法匹配。
然而,API 允許我們指定惰性或非貪婪的方法,使得匹配器可以從范圍的低端開始匹配,匹配兩個連續的aa:
@Test
public void givenBraceQuantifierWithRange_whenMatchesLazily_thenCorrect() {
int matches = runTest("a{2,3}?", "aaaa");
assertEquals(matches, 2);
}
9. 捕獲組
API 還允許我們通過捕獲組將多個字符視為一個單元。它會為捕獲組附加編號,并允許使用這些編號進行反向引用。
在本節中,我們將看到一些如何在 Java 正則表達式 API 中使用捕獲組的示例。
讓我們使用一個捕獲組,僅當輸入文本包含兩個相鄰的數字時才進行匹配:
@Test
public void givenCapturingGroup_whenMatches_thenCorrect() {
int matches = runTest("(\\\\d\\\\d)", "12");
assertEquals(matches, 1);
}
上面匹配的編號是1,使用反向引用告訴匹配器我們要匹配文本的另一部分。這樣,輸入不會有兩個單獨的匹配:
@Test
public void givenCapturingGroup_whenMatches_thenCorrect2() {
int matches = runTest("(\\\\d\\\\d)", "1212");
assertEquals(matches, 2);
}
我們可以獲得一次匹配,但通過反向引用將相同的正則表達式匹配擴展到整個輸入的長度:
@Test
public void givenCapturingGroup_whenMatchesWithBackReference_thenCorrect() {
int matches = runTest("(\\\\d\\\\d)\\\\1", "1212");
assertEquals(matches, 1);
}
我們必須重復正則表達式而不使用反向引用才能實現相同的效果:
@Test
public void givenCapturingGroup_whenMatches_thenCorrect3() {
int matches = runTest("(\\\\d\\\\d)(\\\\d\\\\d)", "1212");
assertEquals(matches, 1);
}
同樣,對于任何其他數量的重復,反向引用可以使匹配器將輸入視為一次匹配:
@Test
public void givenCapturingGroup_whenMatchesWithBackReference_thenCorrect2() {
int matches = runTest("(\\\\d\\\\d)\\\\1\\\\1\\\\1", "12121212");
assertEquals(matches, 1);
}
但如果我們更改最后一個數字,匹配將失敗:
@Test
public void givenCapturingGroupAndWrongInput_whenMatchFailsWithBackReference_thenCorrect() {
int matches = runTest("(\\\\d\\\\d)\\\\1", "1213");
assertFalse(matches > 0);
}
請務必記住轉義反斜杠,它們在 Java 語法中至關重要。
10. 邊界匹配器
Java 正則表達式 API 也支持邊界匹配。如果我們關心匹配應該出現在輸入文本的確切位置,那么這就是我們要找的。在前面的示例中,我們關心的只是是否找到了匹配。
要僅在所需正則表達式在文本開頭為真時匹配,我們使用插入符號^。
由于文本dog可以在開頭找到,這個測試將通過:
@Test
public void givenText_whenMatchesAtBeginning_thenCorrect() {
int matches = runTest("^dog", "dogs are friendly");
assertTrue(matches > 0);
}
以下測試將失敗:
@Test
public void givenTextAndWrongInput_whenMatchFailsAtBeginning_thenCorrect() {
int matches = runTest("^dog", "are dogs are friendly?");
assertFalse(matches > 0);
}
要僅在所需正則表達式在文本末尾為真時匹配,我們使用美元符號$。我們將在以下情況下找到匹配:
@Test
public void givenText_whenMatchesAtEnd_thenCorrect() {
int matches = runTest("dog$", "Man's best friend is a dog");
assertTrue(matches > 0);
}
而在這里我們不會找到匹配:
@Test
public void givenTextAndWrongInput_whenMatchFailsAtEnd_thenCorrect() {
int matches = runTest("dog$", "is a dog man's best friend?");
assertFalse(matches > 0);
}
如果我們只想在找到所需文本時進行匹配,我們在正則表達式的開頭和結尾使用\\b:
空格是一個單詞邊界:
@Test
public void givenText_whenMatchesAtWordBoundary_thenCorrect() {
int matches = runTest("\\bdog\\b", "a dog is friendly");
assertTrue(matches > 0);
}
而這個測試將失敗:
@Test
public void givenTextAndWrongInput_whenMatchFailsAtWordBoundary_thenCorrect() {
int matches = runTest("\\bdog\\b", "snoop dogg is a rapper");
assertFalse(matches > 0);
}
類似地,我們可以匹配整個文本的邊界,而不僅僅是單詞的邊界:
@Test
public void givenText_whenMatchesAtWordAndTextBoundary_thenCorrect() {
int matches = runTest("^dog$", "dog");
assertTrue(matches > 0);
}
11. Pattern 類方法
之前,我們只用基本方式創建了 Pattern
對象。然而,這個類還有一個編譯方法的變體,它接受一組標志以及正則表達式參數,這會影響我們匹配模式的方式。
這些標志只是抽象的整數值。讓我們重載測試類中的 runTest
方法,使它可以將標志作為第三個參數:
public static int runTest(String regex, String text, int flags) {
pattern = Pattern.compile(regex, flags);
matcher = pattern.matcher(text);
int matches = 0;
while (matcher.find()){
matches++;
}
return matches;
}
在本節中,我們將介紹不同支持的標志及其用法。
Pattern.CANON_EQ
此標志啟用了規范等效性。指定時,只有當兩個字符的完整規范分解匹配時,它們才被視為匹配。
考慮帶重音符號的 Unicode 字符é。其復合代碼點是 u00E9
。然而,Unicode 也有其組件字符的獨立代碼點:e
(u0065
)和重音符號 u0301
。在這種情況下,復合字符 u00E9
與字符序列 u0065u0301
無法區分。
默認情況下,匹配不考慮規范等效性:
@Test
public void givenRegexWithoutCanonEq_whenMatchFailsOnEquivalentUnicode_thenCorrect() {
int matches = runTest("\u00E9", "\u0065\u0301");
assertFalse(matches > 0);
}
但如果我們添加了標志,則測試將通過:
@Test
public void givenRegexWithCanonEq_whenMatchesOnEquivalentUnicode_thenCorrect() {
int matches = runTest("\u00E9", "\u0065\u0301", Pattern.CANON_EQ);
assertTrue(matches > 0);
}
Pattern.CASE_INSENSITIVE
此標志允許忽略大小寫進行匹配。默認情況下,匹配時考慮大小寫:
@Test
public void givenRegexWithDefaultMatcher_whenMatchFailsOnDifferentCases_thenCorrect() {
int matches = runTest("dog", "This is a Dog");
assertFalse(matches > 0);
}
使用此標志后,我們可以改變默認行為:
@Test
public void givenRegexWithCaseInsensitiveMatcher_whenMatchesOnDifferentCases_thenCorrect() {
int matches = runTest("dog", "This is a Dog", Pattern.CASE_INSENSITIVE);
assertTrue(matches > 0);
}
我們還可以使用等效的嵌入標志表達式來實現相同的效果:
@Test
public void givenRegexWithEmbeddedCaseInsensitiveMatcher_whenMatchesOnDifferentCases_thenCorrect() {
int matches = runTest("(?i)dog", "This is a Dog");
assertTrue(matches > 0);
}
Pattern.COMMENTS
Java API 允許我們使用 #
在正則表達式中包含注釋。這有助于為可能對其他程序員不太明顯的復雜正則表達式添加文檔說明。
注釋標志使匹配器忽略正則表達式中的任何空格或注釋,并只考慮模式。在默認匹配模式下,以下測試將失敗:
@Test
public void givenRegexWithComments_whenMatchFailsWithoutFlag_thenCorrect() {
int matches = runTest("dog$ #check for word dog at end of text", "This is a dog");
assertFalse(matches > 0);
}
這是因為匹配器將在輸入文本中查找整個正則表達式,包括空格和 #
字符。但是,當我們使用標志時,它會忽略多余的空格,并將 #
開頭的所有文本視為注釋,忽略每行的注釋:
@Test
public void givenRegexWithComments_whenMatchesWithFlag_thenCorrect() {
int matches = runTest("dog$ #check end of text", "This is a dog", Pattern.COMMENTS);
assertTrue(matches > 0);
}
還有一個用于此的替代嵌入標志表達式:
@Test
public void givenRegexWithComments_whenMatchesWithEmbeddedFlag_thenCorrect() {
int matches = runTest("(?x)dog$ #check end of text", "This is a dog");
assertTrue(matches > 0);
}
Pattern.DOTALL
默認情況下,當我們在正則表達式中使用點“.”表達式時,我們會匹配輸入字符串中的每個字符,直到遇到換行符。
使用此標志,匹配將包含行終止符。通過以下示例我們將更好地理解這一點。由于我們希望對匹配的字符串進行斷言,我們將使用 matcher
的 group
方法,它返回上一個匹配項。
首先,讓我們看看默認行為:
@Test
public void givenRegexWithLineTerminator_whenMatchFails_thenCorrect() {
Pattern pattern = Pattern.compile("(.*)");
Matcher matcher = pattern.matcher("this is a text" + System.getProperty("line.separator") + " continued on another line");
matcher.find();
assertEquals("this is a text", matcher.group(1));
}
如我們所見,只有在行終止符之前的輸入部分被匹配。
現在在 dotall
模式中,整個文本(包括行終止符)將被匹配:
@Test
public void givenRegexWithLineTerminator_whenMatchesWithDotall_thenCorrect() {
Pattern pattern = Pattern.compile("(.*)", Pattern.DOTALL);
Matcher matcher = pattern.matcher("this is a text" + System.getProperty("line.separator") + " continued on another line");
matcher.find();
assertEquals("this is a text" + System.getProperty("line.separator") + " continued on another line", matcher.group(1));
}
我們還可以使用嵌入標志表達式啟用 dotall
模式:
@Test
public void givenRegexWithLineTerminator_whenMatchesWithEmbeddedDotall_thenCorrect() {
Pattern pattern = Pattern.compile("(?s)(.*)");
Matcher matcher = pattern.matcher("this is a text" + System.getProperty("line.separator") + " continued on another line");
matcher.find();
assertEquals("this is a text" + System.getProperty("line.separator") + " continued on another line", matcher.group(1));
}
Pattern.LITERAL
在此模式下,匹配器不會給任何元字符、轉義字符或正則表達式語法賦予特殊含義。如果沒有此標志,匹配器將匹配以下正則表達式與任意輸入字符串:
@Test
public void givenRegex_whenMatchesWithoutLiteralFlag_thenCorrect() {
int matches = runTest("(.*)", "text");
assertTrue(matches > 0);
}
這是我們在所有示例中看到的默認行為。但是,使用此標志后,我們將找不到匹配,因為匹配器將查找 (.*)
而不是解釋它:
@Test
public void givenRegex_whenMatchFailsWithLiteralFlag_thenCorrect() {
int matches = runTest("(.*)", "text", Pattern.LITERAL);
assertFalse(matches > 0);
}
現在如果我們添加所需的字符串,測試將通過:
@Test
public void givenRegex_whenMatchesWithLiteralFlag_thenCorrect() {
int matches = runTest("(.*)", "text(.*)", Pattern.LITERAL);
assertTrue(matches > 0);
}
沒有用于啟用文字解析的嵌入標志字符。
Pattern.MULTILINE
默認情況下,^
和 $
元字符分別絕對匹配整個輸入字符串的開始和結束,匹配器忽略任何行終止符:
@Test
public void givenRegex_whenMatchFailsWithoutMultilineFlag_thenCorrect() {
int matches = runTest("dog$", "This is a dog" + System.getProperty("line.separator") + "this is a fox");
assertFalse(matches > 0);
}
通過啟用此標志,匹配器將匹配每一行,而不僅僅是整個輸入文本:
@Test
public void givenRegex_whenMatchesWithMultilineFlag_thenCorrect() {
int matches = runTest("dog$", "This is a dog" + System.getProperty("line.separator") + "this is a fox", Pattern.MULTILINE);
assertTrue(matches > 0);
}
我們還可以使用嵌入表達式來啟用多行模式:
@Test
public void givenRegex_whenMatchesWithEmbeddedMultilineFlag_thenCorrect() {
int matches = runTest("(?m)dog$", "This is a dog" + System.getProperty("line.separator") + "this is a fox");
assertTrue(matches > 0);
}
12. Matcher 類方法
在本節中,我們將學習 Matcher 類中的一些有用方法。為了清晰起見,我們將根據功能對其進行分類。
12.1 索引方法
索引方法提供了有用的索引值,準確顯示在輸入字符串中找到匹配的位置。在下面的測試中,我們將確認輸入字符串中 "dog" 的匹配起始和結束索引:
@Test
public void givenMatch_whenGetsIndices_thenCorrect() {
Pattern pattern = Pattern.compile("dog");
Matcher matcher = pattern.matcher("This dog is mine");
matcher.find();
assertEquals(5, matcher.start());
assertEquals(8, matcher.end());
}
12.2 研究方法
研究方法遍歷輸入字符串,并返回一個布爾值,指示是否找到了模式。常用的方法有 matches 和 lookingAt。
matches 和 lookingAt 方法都嘗試將輸入序列與模式進行匹配。區別在于 matches 要求整個輸入序列完全匹配,而 lookingAt 不需要。
這兩個方法都從輸入字符串的開頭開始匹配:
@Test
public void whenStudyMethodsWork_thenCorrect() {
Pattern pattern = Pattern.compile("dog");
Matcher matcher = pattern.matcher("dogs are friendly");
assertTrue(matcher.lookingAt());
assertFalse(matcher.matches());
}
在這種情況下,matches 方法將返回 true:
@Test
public void whenMatchesStudyMethodWorks_thenCorrect() {
Pattern pattern = Pattern.compile("dog");
Matcher matcher = pattern.matcher("dog");
assertTrue(matcher.matches());
}
12.3 替換方法
替換方法用于替換輸入字符串中的文本。常見的方法有 replaceFirst 和 replaceAll。
replaceFirst 和 replaceAll 方法用于替換與給定正則表達式匹配的文本。顧名思義,replaceFirst 只替換第一次出現的匹配項,replaceAll 替換所有出現的匹配項:
@Test
public void whenReplaceFirstWorks_thenCorrect() {
Pattern pattern = Pattern.compile("dog");
Matcher matcher = pattern.matcher(
"dogs are domestic animals, dogs are friendly");
String newStr = matcher.replaceFirst("cat");
assertEquals(
"cats are domestic animals, dogs are friendly", newStr);
}
替換所有出現的匹配項:
@Test
public void whenReplaceAllWorks_thenCorrect() {
Pattern pattern = Pattern.compile("dog");
Matcher matcher = pattern.matcher(
"dogs are domestic animals, dogs are friendly");
String newStr = matcher.replaceAll("cat");
assertEquals("cats are domestic animals, cats are friendly", newStr);
}
replaceAll 方法允許我們用相同的替換文本替換所有匹配項。如果我們想基于具體情況替換匹配項,則需要使用一種令牌替換技術。
13. 結論
在本文中,我們學習了如何在 Java 中使用正則表達式。我們還探討了 java.util.regex
包中的最重要功能。
若你想提升Java技能,可關注我們的Java培訓課程。