簡介:
java.util.regex是一個用正則表達式所訂制的模式來對字符串進行匹配工作 的類庫包。
它包括兩個類: Pattern和 Matcher
Pattern 一個Pattern是一個正則表達式經編譯後的表現模式。 Matcher 一個Matcher對象是一個狀態機器,它依據Pattern對象做為匹 配模式對字符串展開匹配檢查。
首先一個Pattern實例訂制了一個所用語法與PERL的類似的正則表達式經編譯 後的模式,然後一個Matcher實例在這個給定的Pattern實例的模式控制下進行字 符串的匹配工作。
以下我們就分別來看看這兩個類:
Pattern類:
Pattern的方法如下:
static Pattern compile(String regex)將給定的正則表達式編譯並賦予給Pattern類
static Pattern compile(String regex, int flags)同上,但增加flag參數的指定,可選的flag參數包括:CASE INSENSITIVE,MULTILINE,DOTALL,UNICODE CASE, CANON EQ
int flags()返回當前Pattern的匹配flag參數.
Matcher matcher(CharSequence input)生成一個給定命名的Matcher對象
static boolean matches(String regex, CharSequence input)編譯給定的正則表達式並且對輸入的字串以該正則表達式為模 開展匹配,該方法適合於該正則表達式只會使用一次的情況,也就是只進行一次 匹配工作,因為這種情況下並不需要生成一個Matcher實例。
String pattern()返回該Patter對象所編譯的正則表達式。
String[] split(CharSequence input)將目標字符串按照Pattern裡所包含的正則表達式為模進行分割 。
String[] split(CharSequence input, int limit)作用同上,增加參數limit目的在於要指定分割的段數,如將 limi設為2,那麼目標字符串將根據正則表達式分為割為兩段。
一個正則表達式,也就是一串有特定意義的字符,必須首先要編譯成為一個 Pattern類的實例,這個Pattern對象將會使用 matcher()方法來生成一個 Matcher實例,接著便可以使用該 Matcher實例以編譯的正則表達式為基礎對目 標字符串進行匹配工作,多個Matcher是可以共用一個Pattern對象的。
現在我們先來看一個簡單的例子,再通過分析它來了解怎樣生成一個Pattern 對象並且編譯一個正則表達式,最後根據這個正則表達式將目標字符串進行分割 :
import java.util.regex.*;
public class Replacement{
public static void main(String[] args) throws Exception {
// 生成一個Pattern,同時編譯一個正則表達式
Pattern p = Pattern.compile("[/]+");
//用Pattern的split()方法把字符串按"/"分割
String[] result = p.split(
"Kevin has seen《LEON》seveal times,because it is a good film."
+"/ 凱文已經看過《這個殺手不太冷》幾次了,因為它是一部"
+"好電影。/名詞:凱文。");
for (int i=0; i<result.length; i++)
System.out.println(result[i]);
}
}
輸出結果為:
Kevin has seen《LEON》seveal times,because it is a good film.
凱文已經看過《這個殺手不太冷》幾次了,因為它是一部好電影。
名詞:凱文。
很明顯,該程序將字符串按"/"進行了分段,我們以下再使用 split(CharSequence input, int limit)方法來指定分段的段數,程序改動為:
tring[] result = p.split("Kevin has seen《LEON》seveal times,because it is a good film./ 凱文已經看過《這個殺手不太冷》幾次了 ,因為它是一部好電影。/名詞:凱文。",2);
這裡面的參數"2"表明將目標語句分為兩段。
輸出結果則為:
Kevin has seen《LEON》seveal times,because it is a good film.
凱文已經看過《這個殺手不太冷》幾次了,因為它是一部好電影。/名詞:凱文。
由上面的例子,我們可以比較出java.util.regex包在構造Pattern對象以及 編譯指定的正則表達式的實現手法與我們在上一篇中所介紹的Jakarta-ORO 包在 完成同樣工作時的差別,Jakarta-ORO 包要先構造一個PatternCompiler類對象 接著生成一個Pattern對象,再將正則表達式用該PatternCompiler類的compile ()方法來將所需的正則表達式編譯賦予Pattern類:
PatternCompiler orocom=new Perl5Compiler();
Pattern pattern=orocom.compile("REGULAR EXPRESSIONS");
PatternMatcher matcher=new Perl5Matcher();
但是在java.util.regex包裡,我們僅需生成一個Pattern類,直接使用它的 compile()方法就可以達到同樣的效果: Pattern p = Pattern.compile(" [/]+");
因此似乎java.util.regex的構造法比Jakarta-ORO更為簡潔並容易理解。
Matcher類:
Matcher方法如下:
Matcher appendReplacement(StringBuffer sb, String replacement)將當前匹配子串替換為指定字符串,並且將替換後的子串以及 其之前到上次匹配子串之後的字符串段添加到一個StringBuffer對象裡。
StringBuffer appendTail(StringBuffer sb)將最後一次匹配工作後剩余的字符串添加到一個StringBuffer 對象裡。
int end()返回當前匹配的子串的最後一個字符在原目標字符串中的索引 位置 。
int end(int group)返回與匹配模式裡指定的組相匹配的子串最後一個字符的位置 。
boolean find()嘗試在目標字符串裡查找下一個匹配子串。
boolean find(int start)重設Matcher對象,並且嘗試在目標字符串裡從指定的位置開始 查找下一個匹配的子串。
String group()返回當前查找而獲得的與組匹配的所有子串內容
String group(int group)返回當前查找而獲得的與指定的組匹配的子串內容
int groupCount()返回當前查找所獲得的匹配組的數量。
boolean lookingAt()檢測目標字符串是否以匹配的子串起始。
boolean matches()嘗試對整個目標字符展開匹配檢測,也就是只有整個目標字符 串完全匹配時才返回真值。
Pattern pattern()返回該Matcher對象的現有匹配模式,也就是對應的Pattern 對 象。
String replaceAll(String replacement)將目標字符串裡與既有模式相匹配的子串全部替換為指定的字 符串。
String replaceFirst(String replacement)將目標字符串裡第一個與既有模式相匹配的子串替換為指定的 字符串。
Matcher reset()重設該Matcher對象。
Matcher reset(CharSequence input)重設該Matcher對象並且指定一個新的目標字符串。
int start()返回當前查找所獲子串的開始字符在原目標字符串中的位置。
int start(int group)返回當前查找所獲得的和指定組匹配的子串的第一個字符在原 目標字符串中的位置。
(光看方法的解釋是不是很不好理解?不要急,待會結合例子就比較容易明 白了)
一個Matcher實例是被用來對目標字符串進行基於既有模式(也就是一個給定 的Pattern所編譯的正則表達式)進行匹配查找的,所有往Matcher的輸入都是通 過CharSequence接口提供的,這樣做的目的在於可以支持對從多元化的數據源所 提供的數據進行匹配工作。
我們分別來看看各方法的使用:
★matches()/lookingAt ()/find():
一個Matcher對象是由一個Pattern對象調用其matcher()方法而生成的,一旦 該Matcher對象生成,它就可以進行三種不同的匹配查找操作:
matches()方法嘗試對整個目標字符展開匹配檢測,也就是只有整個目標字符 串完全匹配時才返回真值。
lookingAt ()方法將檢測目標字符串是否以匹配的子串起始。
find()方法嘗試在目標字符串裡查找下一個匹配子串。
以上三個方法都將返回一個布爾值來表明成功與否。
★replaceAll ()/appendReplacement()/appendTail():
Matcher類同時提供了四個將匹配子串替換成指定字符串的方法:
replaceAll()
replaceFirst()
appendReplacement()
appendTail()
replaceAll()與replaceFirst()的用法都比較簡單,請看上面方法的解釋。 我們主要重點了解一下appendReplacement()和appendTail()方法。
appendReplacement(StringBuffer sb, String replacement) 將當前匹配子 串替換為指定字符串,並且將替換後的子串以及其之前到上次匹配子串之後的字 符串段添加到一個StringBuffer對象裡,而appendTail(StringBuffer sb) 方法 則將最後一次匹配工作後剩余的字符串添加到一個StringBuffer對象裡。
例如,有字符串fatcatfatcatfat,假設既有正則表達式模式為 "cat",第一次匹配後調用appendReplacement (sb,"dog"),那麼這時StringBuffer sb的內容為fatdog,也就是 fatcat中的cat被替換為dog並且與匹配子串前的內容加到sb裡,而第二次匹配後 調用appendReplacement(sb,"dog"),那麼sb的內容就變為 fatdogfatdog,如果最後再調用一次appendTail(sb),那麼sb最終的內容將是 fatdogfatdogfat。
還是有點模糊?那麼我們來看個簡單的程序:
//該例將把句子裡的"Kelvin"改為"Kevin"
import java.util.regex.*;
public class MatcherTest{
public static void main(String[] args)
throws Exception {
//生成Pattern對象並且編譯一個簡單的正則表達式 "Kelvin"
Pattern p = Pattern.compile("Kevin");
//用Pattern類的matcher()方法生成一個Matcher對象
Matcher m = p.matcher("Kelvin Li and Kelvin Chan are both working in Kelvin Chen's KelvinSoftShop company");
StringBuffer sb = new StringBuffer();
int i=0;
//使用find()方法查找第一個匹配的對象
boolean result = m.find();
//使用循環將句子裡所有的kelvin找出並替換再將內容加到sb裡
while(result) {
i++;
m.appendReplacement(sb, "Kevin");
System.out.println("第"+i+"次匹配後sb的內容 是:"+sb);
//繼續查找下一個匹配對象
result = m.find();
}
//最後調用appendTail()方法將最後一次匹配後的剩余字符串加到sb裡 ;
m.appendTail(sb);
System.out.println("調用m.appendTail(sb)後sb的最終內容 是:"+ sb.toString());
}
}
最終輸出結果為:
第1次匹配後sb的內容是:Kevin
第2次匹配後sb的內容是:Kevin Li and Kevin
第3次匹配後sb的內容是:Kevin Li and Kevin Chan are both working in Kevin
第4次匹配後sb的內容是:Kevin Li and Kevin Chan are both working in Kevin Chen's Kevin
調用m.appendTail(sb)後sb的最終內容是:Kevin Li and Kevin Chan are both working in Kevin Chen's KevinSoftShop company.
看了上面這個例程是否對appendReplacement(),appendTail()兩個方法的使 用更清楚呢,如果還是不太肯定最好自己動手寫幾行代碼測試一下。
★group()/group(int group)/groupCount():
該系列方法與我們在上篇介紹的Jakarta-ORO中的MatchResult .group()方法 類似(有關Jakarta-ORO請參考上篇的內容),都是要返回與組匹配的子串內容, 下面代碼將很好解釋其用法:
import java.util.regex.*;
public class GroupTest{
public static void main(String[] args)
throws Exception {
Pattern p = Pattern.compile("(ca)(t)");
Matcher m = p.matcher("one cat,two cats in the yard");
StringBuffer sb = new StringBuffer();
boolean result = m.find();
System.out.println("該次查找獲得匹配組的數量為: "+m.groupCount());
for(int i=1;i<=m.groupCount();i++){
System.out.println("第"+i+"組的子串內容為: "+m.group(i));
}
}
}
輸出為:
該次查找獲得匹配組的數量為:2
第1組的子串內容為:ca
第2組的子串內容為:t
Matcher對象的其他方法因比較好理解且由於篇幅有限,請讀者自己編程驗證 。
一個檢驗Email地址的小程序:
最後我們來看一個檢驗Email地址的例程,該程序是用來檢驗一個輸入的 EMAIL地址裡所包含的字符是否合法,雖然這不是一個完整的EMAIL地址檢驗程序 ,它不能檢驗所有可能出現的情況,但在必要時您可以在其基礎上增加所需功能 。
import java.util.regex.*; public class Email { public static void main(String[] args) throws Exception { String input = args[0]; // 檢測輸入的 EMAIL 地址是否以 非法符號"."或"@"作為起始字符 Pattern p = Pattern.compile("^\\.|^\\@"); Matcher m = p.matcher(input); if (m.find()){ System.err.println("EMAIL 地址不能以'.'或'@'作為起始字符"); } // 檢測是否以"www."為起始 p = Pattern.compile("^www\\."); m = p.matcher(input); if (m.find()) { System.out.println("EMAIL 地址不能以'www.'起始"); } // 檢測是否包含非法字符 p = Pattern.compile("[^A-Za-z0-9\\.\\@_\\-~#]+"); m = p.matcher(input); StringBuffer sb = new StringBuffer(); boolean result = m.find(); boolean deletedIllegalChars = false; while(result) { // 如果找到了非法字符那麼就設下標記 deletedIllegalChars = true; // 如果裡面包含非法字符如冒號雙引號等,那麼就把他們消去,加到 SB 裡面 m.appendReplacement(sb, ""); result = m.find(); } m.appendTail(sb); input = sb.toString(); if (deletedIllegalChars) { System.out.println("輸入的 EMAIL 地址裡包含有冒號、逗號等非法字符,請修改"); System.out.println("您現在的輸入為 : "+args[0]); System.out.println("修改後合法的地址應類似 : "+input); } } }
例如,我們在命令行輸入:java Email [email protected]
那麼輸出結果將會是:EMAIL地址不能以'www.'起始
如果輸入的EMAIL為163.net
則輸出為:EMAIL地址不能以'.'或'@'作為起始字符
當輸入為:cgjmail#$%@163.net
那麼輸出就是:
輸入的EMAIL地址裡包含有冒號、逗號等非法字符,請修改
您現在的輸入為: cgjmail#$%@163.net修改後合法的地址應類似: [email protected]總結:本文介紹了jdk1.4.0-beta3裡正則表達式庫-- java.util.regex中的類以及其方法,如果結合與上一篇中所介紹的Jakarta-ORO API作比較,讀者會更容易掌握該API的使用,當然該庫的性能將在未來的日子裡 不斷擴展,希望獲得最新信息的讀者最好到及時到SUN的網站去了解。結束語: 本來計劃再多寫一篇介紹一下需付費的正則表達式庫中較具代表性的作品,但覺 得既然有了免費且優秀的正則表達式庫可以使用,何必還要去找需付費的呢,相 信很多讀者也是這麼想的:,所以有興趣了解更多其他的第三方正則表達式庫的 朋友可以自己到網上查找或者到我在參考資料裡提供的網址去看看。