閱讀986 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Java 正則表達式

Java 提供了功能強大的正則表達式API,在java.util.regex 包下。本教程介紹如何使用正則表達式API。

正則表達式

一個正則表達式是一個用於文本搜索的文本模式。換句話說,在文本中搜索出現的模式。例如,你可以用正則表達式搜索網頁中的郵箱地址或超鏈接。

正則表達式示例

下麵是一個簡單的Java正則表達式的例子,用於在文本中搜索 http://

String text    =
        "This is the text to be searched " +
        "for occurrences of the http:// pattern.";
String pattern = ".*http://.*";
boolean matches = Pattern.matches(pattern, text);
System.out.println("matches = " + matches);

示例代碼實際上沒有檢測找到的 http:// 是否是一個合法超鏈接的一部分,如包含域名和後綴(.com,.net 等等)。代碼隻是簡單的查找字符串 http:// 是否出現。

Java6 中關於正則表達式的API

本教程介紹了Java6 中關於正則表達式的API。

Pattern (java.util.regex.Pattern)

類 java.util.regex.Pattern 簡稱 Pattern, 是Java正則表達式API中的主要入口,無論何時,需要使用正則表達式,從Pattern 類開始

Pattern.matches()

檢查一個正則表達式的模式是否匹配一段文本的最直接方法是調用靜態方法Pattern.matches(),示例如下:

String text    =
        "This is the text to be searched " +
        "for occurrences of the pattern.";
String pattern = ".*is.*";
boolean matches = Pattern.matches(pattern, text);
System.out.println("matches = " + matches);

上麵代碼在變量 text 中查找單詞 “is” 是否出現,允許”is” 前後包含 0或多個字符(由 .* 指定)
Pattern.matches() 方法適用於檢查 一個模式在一個文本中出現一次的情況,或適用於Pattern類的默認設置。

如果需要匹配多次出現,甚至輸出不同的匹配文本,或者隻是需要非默認設置。需要通過Pattern.compile() 方法得到一個Pattern 實例。

Pattern.compile()

如果需要匹配一個正則表達式在文本中多次出現,需要通過Pattern.compile() 方法創建一個Pattern對象。示例如下

String text    =
        "This is the text to be searched " +
        "for occurrences of the http:// pattern.";
String patternString = ".*http://.*";
Pattern pattern = Pattern.compile(patternString);

可以在Compile 方法中,指定一個特殊標誌:
Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
Pattern 類包含多個標誌(int 類型),這些標誌可以控製Pattern 匹配模式的方式。上麵代碼中的標誌使模式匹配是忽略大小寫

Pattern.matcher()

一旦獲得了Pattern對象,接著可以獲得Matcher對象。Matcher 示例用於匹配文本中的模式.示例如下
Matcher matcher = pattern.matcher(text);

Matcher類有一個matches()方法,可以檢查文本是否匹配模式。以下是關於Matcher的一個完整例子

String text    =
        "This is the text to be searched " +
        "for occurrences of the http:// pattern.";
String patternString = ".*http://.*";
Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(text);
boolean matches = matcher.matches();
System.out.println("matches = " + matches);

Pattern.split()

Pattern 類的 split()方法,可以用正則表達式作為分隔符,把文本分割為String類型的數組。示例:

String text = "A sep Text sep With sep Many sep Separators";
String patternString = "sep";
Pattern pattern = Pattern.compile(patternString);
String[] split = pattern.split(text);
System.out.println("split.length = " + split.length);
for(String element : split){
    System.out.println("element = " + element);
}

上例中把text 文本分割為一個包含5個字符串的數組。

Pattern.pattern()

Pattern 類的 pattern 返回用於創建Pattern 對象的正則表達式,示例:

String patternString = "sep";
Pattern pattern = Pattern.compile(patternString);
String pattern2 = pattern.pattern();

上麵代碼中 pattern2 值為sep ,與patternString 變量相同。

Matcher (java.util.regex.Matcher)

java.util.regex.Matcher 類用於匹配一段文本中多次出現一個正則表達式,Matcher 也適用於多文本中匹配同一個正則表達式。

Matcher 有很多有用的方法,詳細請參考官方JavaDoc。這裏隻介紹核心方法。

以下代碼演示如何使用Matcher

String text    =
        "This is the text to be searched " +
        "for occurrences of the http:// pattern.";
String patternString = ".*http://.*";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(text);
boolean matches = matcher.matches();

首先創建一個Pattern,然後得到Matcher ,調用matches() 方法,返回true 表示模式匹配,返回false表示不匹配。
可以用Matcher 做更多的事。

創建Matcher

通過Pattern 的matcher() 方法創建一個Matcher。

String text    =
        "This is the text to be searched " +
        "for occurrences of the http:// pattern.";

String patternString = ".*http://.*";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(text);

matches()

Matcher 類的 matches() 方法用於在文本中匹配正則表達式

boolean matches = matcher.matches();

如果文本匹配正則表達式,matches() 方法返回true。否則返回false。

matches() 方法不能用於查找正則表達式多次出現。如果需要,請使用find(), start() 和 end() 方法。

lookingAt()

lookingAt() 與matches() 方法類似,最大的不同是,lookingAt()方法對文本的開頭匹配正則表達式;而
matches() 對整個文本匹配正則表達式。換句話說,如果正則表達式匹配文本開頭而不匹配整個文本,lookingAt() 返回true,而matches() 返回false。 示例:

String text    =
        "This is the text to be searched " +
        "for occurrences of the http:// pattern.";
String patternString = "This is the";
Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(text);
System.out.println("lookingAt = " + matcher.lookingAt());
System.out.println("matches   = " + matcher.matches());

上麵的例子分別對文本開頭和整個文本匹配正則表達式 “this is the”. 匹配文本開頭的方法(lookingAt()) 返回true。

對整個文本匹配正則表達式的方法 (matches()) 返回false,因為 整個文本包含多餘的字符,而 正則表達式要求文本精確匹配”this is the”,前後又不能有額外字符。

find() + start() + end()

find() 方法用於在文本中查找出現的正則表達式,文本是創建Matcher時,通過 Pattern.matcher(text) 方法傳入的。如果在文本中多次匹配,find() 方法返回第一個,之後每次調用 find() 都會返回下一個。

start() 和 end() 返回每次匹配的字串在整個文本中的開始和結束位置。實際上, end() 返回的是字符串末尾的後一位,這樣,可以在把 start() 和 end() 的返回值直接用在String.substring() 裏。

String text    =
        "This is the text which is to be searched " +
        "for occurrences of the word 'is'.";
String patternString = "is";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(text);
int count = 0;
while(matcher.find()) {
    count++;
    System.out.println("found: " + count + " : "  + matcher.start() + " - " + matcher.end());
}

這個例子在文本中找到模式 “is” 4次,輸出如下:

 found: 1 : 2 - 4
 found: 2 : 5 - 7
 found: 3 : 23 - 25
 found: 4 : 70 - 72

reset()

reset() 方法會重置Matcher 內部的 匹配狀態。當find() 方法開始匹配時,Matcher 內部會記錄截至當前查找的距離。調用 reset() 會重新從文本開頭查找。

也可以調用 reset(CharSequence) 方法. 這個方法重置Matcher,同時把一個新的字符串作為參數傳入,用於代替創建 Matcher 的原始字符串。

group()

假設想在一個文本中查找URL鏈接,並且想把找到的鏈接提取出來。當然可以通過 start()和 end()方法完成。但是用group()方法更容易些。

分組在正則表達式中用括號表示,例如:
(John)
此正則表達式匹配John, 括號不屬於要匹配的文本。括號定義了一個分組。當正則表達式匹配到文本後,可以訪問分組內的部分。

使用group(int groupNo) 方法訪問一個分組。一個正則表達式可以有多個分組。每個分組由一對括號標記。想要訪問正則表達式中某分組匹配的文本,可以把分組編號傳入 group(int groupNo)方法。

group(0) 表示整個正則表達式,要獲得一個有括號標記的分組,分組編號應該從1開始計算。

String text    =  "John writes about this, and John writes about that," +
                        " and John writes about everything. "  ;
String patternString1 = "(John)";
Pattern pattern = Pattern.compile(patternString1);
Matcher matcher = pattern.matcher(text);
while(matcher.find()) {
    System.out.println("found: " + matcher.group(1));
}
 以上代碼在文本中搜索單詞John.從每個匹配文本中,提取分組1,就是由括號標記的部分。輸出如下
 found: John
 found: John
 found: John

多分組

上麵提到,一個正則表達式可以有多個分組,例如:
(John) (.+?)
這個表達式匹配文本”John” 後跟一個空格,然後跟1個或多個字符,最後跟一個空格。你可能看不到最後的空格。

這個表達式包括一些字符有特別意義。字符 點 . 表示任意字符。 字符 + 表示出現一個或多個,和. 在一起表示 任何字符,出現一次或多次。字符? 表示 匹配盡可能短的文本。

完整代碼如下

String text    =
          "John writes about this, and John Doe writes about that," +
                  " and John Wayne writes about everything."
        ;
String patternString1 = "(John) (.+?) ";
Pattern pattern = Pattern.compile(patternString1);
Matcher matcher = pattern.matcher(text);
while(matcher.find()) {
    System.out.println("found: " + matcher.group(1) +
                       " "       + matcher.group(2));
}
 注意代碼中引用分組的方式。代碼輸出如下
 found: John writes
 found: John Doe
 found: John Wayne

嵌套分組

在正則表達式中分組可以嵌套分組,例如
((John) (.+?))
這是之前的例子,現在放在一個大分組裏.(表達式末尾有一個空格)。

當遇到嵌套分組時, 分組編號是由左括號的順序確定的。上例中,分組1 是那個大分組。分組2 是包括John的分組,分組3 是包括 .+? 的分組。當需要通過groups(int groupNo) 引用分組時,了解這些非常重要。

以下代碼演示如何使用嵌套分組

String text    =
          "John writes about this, and John Doe writes about that," +
                  " and John Wayne writes about everything."
        ;
String patternString1 = "((John) (.+?)) ";
Pattern pattern = Pattern.compile(patternString1);
Matcher matcher = pattern.matcher(text);
while(matcher.find()) {
    System.out.println("found:   ");
}
 輸出如下
 found:
 found:
 found: 

replaceAll() + replaceFirst()

replaceAll() 和 replaceFirst() 方法可以用於替換Matcher搜索字符串中的一部分。replaceAll() 方法替換全部匹配的正則表達式,replaceFirst() 隻替換第一個匹配的。

在處理之前,Matcher 會先重置。所以這裏的匹配表達式從文本開頭開始計算。

示例如下

String text    =
          "John writes about this, and John Doe writes about that," +
                  " and John Wayne writes about everything."
        ;
String patternString1 = "((John) (.+?)) ";
Pattern pattern = Pattern.compile(patternString1);
Matcher matcher = pattern.matcher(text);

String replaceAll = matcher.replaceAll("Joe Blocks ");
System.out.println("replaceAll   = " + replaceAll);

String replaceFirst = matcher.replaceFirst("Joe Blocks ");
System.out.println("replaceFirst = " + replaceFirst);

輸出如下
replaceAll = Joe Blocks about this, and Joe Blocks writes about that,
and Joe Blocks writes about everything.
replaceFirst = Joe Blocks about this, and John Doe writes about that,
and John Wayne writes about everything.

輸出中的換行和縮進是為了可讀而增加的。

注意第1個字符串中所有出現 John 後跟一個單詞 的地方,都被替換為 Joe Blocks 。第2個字符串中,隻有第一個出現的被替換。

appendReplacement() + appendTail()

appendReplacement() 和 appendTail() 方法用於替換輸入文本中的字符串短語,同時把替換後的字符串附加到一個 StringBuffer 中。

當find() 方法找到一個匹配項時,可以調用 appendReplacement() 方法,這會導致輸入字符串被增加到StringBuffer 中,而且匹配文本被替換。 從上一個匹配文本結尾處開始,直到本次匹配文本會被拷貝。

appendReplacement() 會記錄拷貝StringBuffer 中的內容,可以持續調用find(),直到沒有匹配項。

直到最後一個匹配項目,輸入文本中剩餘一部分沒有拷貝到 StringBuffer. 這部分文本是從最後一個匹配項結尾,到文本末尾部分。通過調用 appendTail() 方法,可以把這部分內容拷貝到 StringBuffer 中.

String text    =
          "John writes about this, and John Doe writes about that," +
                  " and John Wayne writes about everything."
        ;

String patternString1 = "((John) (.+?)) ";
Pattern      pattern      = Pattern.compile(patternString1);
Matcher      matcher      = pattern.matcher(text);
StringBuffer stringBuffer = new StringBuffer();

while(matcher.find()){
    matcher.appendReplacement(stringBuffer, "Joe Blocks ");
    System.out.println(stringBuffer.toString());
}
matcher.appendTail(stringBuffer);
System.out.println(stringBuffer.toString());
 注意我們在while循環中調用appendReplacement() 方法。在循環完畢後調用appendTail()。
 代碼輸出如下:
 Joe Blocks
 Joe Blocks about this, and Joe Blocks
 Joe Blocks about this, and Joe Blocks writes about that, and Joe Blocks
 Joe Blocks about this, and Joe Blocks writes about that, and Joe Blocks
 writes about everything.

Java 正則表達式語法

為了更有效的使用正則表達式,需要了解正則表達式語法。正則表達式語法很複雜,可以寫出非常高級的表達式。隻有通過大量的練習才能掌握這些語法規則。

本篇文字,我們將通過例子了解正則表達式語法的基礎部分。介紹重點將會放在為了使用正則表達式所需要了解的核心概念,不會涉及過多的細節。詳細解釋,參見 Java DOC 中的 Pattern 類.

基本語法

在介紹高級功能前,我們先快速瀏覽下正則表達式的基本語法。

字符

是正則表達式中最經常使用的的一個表達式,作用是簡單的匹配一個確定的字符。例如:
John
這個簡單的表達式將會在一個輸入文本中匹配John文本。
可以在表達式中使用任意英文字符。也可以使用字符對於的8進製,16進製或unicode編碼表示。例如:
101
\x41
\u0041

以上3個表達式 都表示大寫字符A。第一個是8進製編碼(101),第2個是16進製編碼(41),第3個是unicode編碼(0041).

字符分類

字符分類是一種結構,可以針對多個字符匹配而不隻是一個字符。換句話說,一個字符分類匹配輸入文本中的一個字符,對應字符分類中多個允許字符。例如,你想匹配字符 a,b 或c,表達式如下:
[abc]

用一對方括號[] 表示字符分類。方括號本身並不是要匹配的一部分。

可以用字符分類完成很多事。例如想要匹配單詞John,首字母可以為大寫和小寫J.
[Jj]ohn

字符分類[Jj] 匹配J或j,剩餘的 ohn 會準確匹配字符ohn.

預定義字符分類

正則表達式中有一些預定義的字符分類可以使用。例如, \d 表示任意數字, \s 表示任意空白字符,\w 表示任意單詞字符。
預定義字符分類不需要括在方括號裏,當然也可以組合使用
\d
[\d\s]

第1個匹配任意數字,第2個匹配任意數字或空白符。
完整的預定義字符分類列表,在本文最後列出。

邊界匹配

正則表達式支持匹配邊界,例如單詞邊界,文本的開頭或末尾。例如,\w 匹配一個單詞,^匹配行首,$ 匹配行尾。
^This is a single line$
上麵的表達式匹配一行文本,隻有文本 This is a single line。注意其中的行首和行尾標誌,表示不能有任何文本在文本的前麵後後麵,隻能是行首和行尾。
完整的匹配邊界列表,在本文最後列出。

量詞匹配

量詞可以匹配一個表達式多次出現。例如下列表達式匹配字母A 出現0次或多次。
A*

量詞 * 表示0次或多次。+ 表示1次或多次。? 表示0次或1次。還有些其他量詞,參見本文後麵的列表。

量詞匹配分為 饑餓模式,貪婪模式,獨占模式。饑餓模式 匹配盡可能少的文本。貪婪模式匹配盡可能多的文本。獨占模式匹配盡可能多的文本,甚至導致剩餘表達式匹配失敗。

以下演示饑餓模式,貪婪模式,獨占模式區別。假設以下文本:
John went for a walk, and John fell down, and John hurt his knee.

饑餓模式下 表達式:
John.*?
這個表達式匹配John 後跟0個或多個字符。 . 表示任意字符。* 表示0或多次。? 跟在 * 後麵,表示 * 采用饑餓模式。
饑餓模式下,量詞隻會匹配盡可能少的字符,即0個字符。上例中的表達式將會匹配單詞John,在輸入文本中出現3次。

如果改為貪婪模式,表達式如下:
John.*
貪婪模式下,量詞會匹配盡可能多的字符。現在表達式會匹配第一個出現的John,以及在貪婪模式下 匹配剩餘的所有字符。這樣,隻有一個匹配項。

最後,我們改為獨占模式:
John.*+hurt
*後跟+ 表示獨占模式量詞。

這個表達式在輸入文本中沒有匹配項,盡管文本中包括 John 和 hurt. 為什麼會這樣? 因為 .*+ 是獨占模式。與貪婪模式下,盡可能多的匹配文本,以使整個表達式匹配不同。獨占模式會盡可能的多的匹配,但不考慮表達式剩餘部分是否能匹配上。

.*+ 將會匹配第一個John之後的所有字符,這會導致表達式中剩餘的 hurt 沒有匹配項。如果改為貪婪模式,會有一個匹配項。表達式如下:
John.*hurt

邏輯操作符

正則表達式支持少量的邏輯運算(與,或,非)。
與操作是默認的,表達式 John ,意味著J 與 o與h與n。
或操作需要顯示指定,用 | 表示。例如表達式 John|hurt 意味著John 或 hurt 。

字符

. 任意英文字母
\\ 反斜杠, 單獨的反斜杠做為轉義字符,與其他特殊字符一起使用。如果想匹配反斜杠本身,需要轉義。兩個反斜杠實際匹配一個反斜杠n字符的8進製表示.n 在0至7之間取值
nn 字符的8進製表示.n 在0至7之間取值
mnn 字符的8進製表示. m 在0至3之間取值, n 在0至7之間取值
\xhh 字符的16進製表示.
\uhhhh 字符的16進製表示 0xhhhh. 對應unicode 編碼字符
\t 縮進符.
\n 換行符 (unicode: ‘\u000A’)
\r 回車符 (unicode: ‘\u000D’)
\f 製表符 (unicode: ‘\u000C’)
\a 警報(鈴聲)字符 (unicode: ‘\u0007′)
\e 轉義符 (unicode: ‘\u001B’)
\cx 控製符 x

字符分類

[abc] 匹配 a, 或 b 或 c
[^abc] 匹配不是a,b,c 的字符,是否定匹配
[a-zA-Z] 匹配a 到 z ,A到Z 直接的字符,是範圍匹配
[a-d[m-p]] 匹配a到d之間字符或 m到p之間字符,是並集匹配
[a-z&&[def]] 匹配 d, e, 或 f. 是交集匹配 (這裏是在範圍 a-z和字符def之間取交集).
[a-z&&[^bc]] 匹配a-z 之間所有字符,排除bc的字符。是減法匹配
[a-z&&[^m-p]] 匹配a-z 之間所有字符,排除m-p之間的字符是減法匹配

內置字符分類

. 匹配任意一個字符,根據創建Pattern是傳入的標誌,可能匹配行結尾符
\d 匹配任意數字 [0-9]
\D 匹配任意非數字 [^0-9]
\s 匹配任意空白符 (空格, 縮進, 換行,回車)
\S 匹配任意非空白符
\w 匹配任意單詞
\W 匹配任意非單詞

邊界匹配

^ 匹配行首
$ 匹配行尾
\b 匹配單詞邊界
\B 匹配非單詞邊界
\A 匹配文本開頭
\G 匹配前一匹配項結尾
\Z Matches the end of the input text except the final terminator if any
\z 匹配文本結尾

量詞

貪婪模式 饑餓模式 獨占模式
X? X?? X?+ 匹配0或1次
X* X*? X*+ 匹配0或多次
X+ X+? X++ 匹配1或多次
X{n} X{n}? X{n}+ 匹配n次
X{n,} X{n,}? X{n,}+ 匹配最少n次
X{n, m} X{n, m}? X{n, m}+ 匹配最少n次,最多m次

最後更新:2017-05-23 18:02:21

  上一篇:go  原子循環計數器
  下一篇:go  JVM性能優化(一)JVM技術入門