現在JDK1.4里終于有了自己的正則表達式API包,JAVA程序員可以免去找第三方提供的正則表達式庫的周折了,我們現在就馬上來了解一下這個SUN提供的遲來恩物-?-對我來說確實如此。?
1.簡介:??
java.util.regex是一個用正則表達式所訂制的模式來對字符串進行匹配工作的類庫包。?
它包括兩個類:
Pattern和Matcher?Pattern?一個Pattern是一個正則表達式經
編譯后的表現模式。??
Matcher?一個Matcher對象是一個
狀態機器,它依據Pattern對象做為匹配模式對字符串展開匹配檢查。?首先一個Pattern實例訂制了一個所用語法與PERL的類似的正則表達式經編譯后的模式,然后一個Matcher實例在這個給定的Pattern實例的模式控制下進行字符串的匹配工作。?
以下我們就分別來看看這兩個類:?
2.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更為簡潔并容易理解。?
3.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對象的其他方法因比較好理解且由于篇幅有限,請讀者自己編程驗證。?
4.一個檢驗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?www.kevin@163.net?
那么輸出結果將會是:EMAIL地址不能以'www.'起始?
如果輸入的EMAIL為@kevin@163.net?
則輸出為:EMAIL地址不能以'.'或'@'作為起始字符?
當輸入為:cgjmail#$%@163.net?
那么輸出就是:?
輸入的EMAIL地址里包含有冒號、逗號等非法字符,請修改?
您現在的輸入為:?cgjmail#$%@163.net?
修改后合法的地址應類似:?cgjmail@163.net?
5.總結:?
本文介紹了jdk1.4.0-beta3里正則表達式庫--java.util.regex中的類以及其方法,如果結合與上一篇中所介紹的Jakarta-ORO?API作比較,讀者會更容易掌握該API的使用,當然該庫的性能將在未來的日子里不斷擴展,希望獲得最新信息的讀者最好到及時到SUN的網站去了解。?
6.結束語:?
本來計劃再多寫一篇介紹一下需付費的正則表達式庫中較具代表性的作品,但覺得既然有了免費且優秀的正則表達式庫可以使用,何必還要去找需付費的呢,相信很多讀者也是這么想的:,所以有興趣了解更多其他的第三方正則表達式庫的朋友可以自己到網上查找或者到我在參考資料里提供的網址去看看。?
參考資料?
java.util.regex的幫助文檔?
Dana?Nourie?和Mike?McCloskey所寫的Regular?Expressions?and?the?Java??Programming?Language?
需要更多的第三方正則表達式資源以及基于它們所開發的應用程序請看
http://www.meurrens.org/ip-Links/java/regex/index.html?
Java's java.util.regex 包包括:Pattern類、Matcher類、MatchResult接口和PatternSyntaxException異常:
- Pattern 對象代表了編譯了的正則表達式(A compiled representation of a regular expression.)。
- Matcher ,An engine that performs match operations on a
character sequence
by interpreting a Pattern
.
- MatchResult接口:The result of a match operation.
- PatternSyntaxException對象,描述非法的regex patterns,Unchecked exception thrown to indicate a syntax error in a regular-expression pattern.
?同時還需要了解是CharSequence,JDK 1.4定義的新的接口,它提供了String和StringBuffer這兩個類的字符序列的抽象
interface CharSequence {
charAt(int i);
length();
subSequence(int start, int end);
toString();
}
為了實現這個新的CharSequence接口,String,StringBuffer以及CharBuffer都作了修改,很多的正則表達式的操作都要拿CharSequence作參數。
Matcher類的幾個重要的方法:
- boolean matches():Pattern匹配整個字符串
- boolean lookingAt():Pattern匹配字符串的開頭
- boolean find():發現CharSequence里的,與pattern相匹配的多個字符序列
boolean find(int start):告訴方法從參數start位置開始查找
group的概念
Group是指里用括號括起來的,能被后面的表達式調用的正則表達式。
Group 0 表示整個表達式,group 1表示第一個被括起來的group,以此類推。所以;
A(B(C))D
里面有三個group:group 0是ABCD, group 1是BC,group 2是C。
你可以用下述Matcher方法來使用group:
- public int groupCount( )返回matcher對象中的group的數目。不包括group0。
- public String group( ) 返回上次匹配操作(比方說find( ))的group 0(整個匹配)
- public String group(int i)返回上次匹配操作的某個group。如果匹配成功,但是沒能找到group,則返回null。
- public int start(int group)返回上次匹配所找到的group的開始位置。
- public int end(int group)返回上次匹配所找到的group的結束位置,最后一個字符的下標加一。
- split( ) 是指將以正則表達式為界,將字符串分割成String數組,如果帶有limit參數,split( )會限定分割的次數。
- replaceFirst(String replacement)將字符串里,第一個與模式相匹配的子串替換成replacement。
- replaceAll(String replacement),將輸入字符串里所有與模式相匹配的子串全部替換成replacement。
- appendReplacement(StringBuffer sbuf, String replacement)對sbuf進行逐次替換,而不是像replaceFirst( )或replaceAll( )那樣,只替換第一個或全部子串。這是個非常重要的方法,因為replacement(replaceFirst( )和replaceAll( )只允許用固定的字符串來充當replacement)。有了這個方法,你就可以編程區分group,從而實現更強大的替換功能。
- 調用完appendReplacement( )之后,為了把剩余的字符串拷貝回去,必須調用appendTail(StringBuffer sbuf, String replacement)。
?
使用group可以做到逐步縮小匹配的范圍,直至精確匹配的目的。
start( )和end( ):如果匹配成功,start( )會返回此次匹配的開始位置,end( )會返回此次匹配的結束位置,即最后一個字符的下標加一。如果之前的匹配不成功(或者沒匹配),那么無論是調用start( )還是end( ),都會引發一個IllegalStateException。
二、4大使用方法:
查詢
String str="abc efg ABC";
String regEx="a|f"; //表示a或f
Pattern p=Pattern.compile(regEx);
Matcher m=p.matcher(str);
boolean rs=m.find();
如果str中有regEx,那么rs為true,否則為flase。如果想在查找時忽略大小寫,則可以寫成Pattern p=Pattern.compile(regEx,Pattern.CASE_INSENSITIVE);
提取
String regEx=".+\(.+)$";
String str="c:\dir1\dir2\name.txt";
Pattern p=Pattern.compile(regEx);
Matcher m=p.matcher(str);
boolean rs=m.find();
for(int i=1;i<=m.groupCount();i++){
System.out.println(m.group(i));
}
以上的執行結果為name.txt,提取的字符串儲存在m.group(i)中,其中i最大值為m.groupCount();
?
分割
String regEx="::";
Pattern p=Pattern.compile(regEx);
String[] r=p.split("xd::abc::cde");
執行后,r就是{"xd","abc","cde"},其實分割時還有跟簡單的方法:
String str="xd::abc::cde";
String[] r=str.split("::");
?
替換或者刪除
String regEx="a+"; //表示一個或多個a
Pattern p=Pattern.compile(regEx);
Matcher m=p.matcher("aaabbced a ccdeaa");
String s=m.replaceAll("A");
結果為"Abbced A ccdeA"
如果寫成空串,既可達到刪除的功能,比如:
String s=m.replaceAll("");
結果為"bbced ccde"
?
三、一個實際的例子
下面的函數是一個實際應用的例子,需求是需要將抓取的網頁中的<img src=''http://aa.bb.cc.com/images/**.jpg"> 中的地址,保存到一個地址列表中(以供抓取圖片使用),并將絕對地址替換成本地的相對地址,即<img src="images/**.jpg">。
public static Map<String, String> getImagesURL(String description) {
????? Map<String, String> map = new HashMap<String, String>();
??????? // img 的正則表達式:匹配<img>標簽???????
??????? String imgPattern = "<\\s*img\\s+([^>]+)\\s*>";
??????? Pattern pattern1 = Pattern.compile(imgPattern, Pattern.CASE_INSENSITIVE);
??????? Matcher matcher = pattern1.matcher(description);
??????? // img src元素的正則表達式:匹配img標簽內的src屬性
??????? String srcPattern = "\\s*src\\s*=\\s*\"([^\"]+)\\s*\"";
????????Pattern pattern2 = Pattern.compile(srcPattern, Pattern.CASE_INSENSITIVE);
??????? while (matcher.find()) {
?????????? //group()返回符合表達式的內容
??????????? Matcher matcher2 = pattern2 .matcher(matcher.group());
??????????? // 一定要find(),這是實際的匹配動作
??????????? if (matcher2.find()) {
??????????????? String src = matcher2.group();
??????????????? log.info(src);
??????????????? int i2 = src.lastIndexOf('/');
??????????????? int i1 = src.indexOf("http");
??????????????? if (i1 != -1) {
??????????????????? map.put(src.substring(i2 + 1, src.length() - 1), src
??????????????????????????? .substring(i1, src.length() - 1));
??????????????? }
??????????? }
??????? }
??????? log.debug("圖片:" + map);
??????? return map;
??? }
?
整體思路是先匹配到img標簽,然后匹配src屬性,并修改src的屬性
"<\\s*img\\s+([^>]+)\\s*>" 的解釋:
\\s* :0 或多個空格?? \\s+: 至少一個空格
([^>]+):指的是到>為止的所有的元素,至少一個
?
常用的正則表達式(參考jdk的regex文檔)
字符集類
.??????????????????????????? 表示任意一個字符
[abc]???????????????????? 表示字符a,b,c中的任意一個(與a|b|c相同)
[^abc]?????????????????? 除a,b,c之外的任意一個字符(否定)
[a-zA-Z]??????????????? 從a到z或A到Z當中的任意一個字符(范圍)
[abc[hij]]????????????? a,b,c,h,i,j中的任意一個字符(與a|b|c|h|i|j相同)(并集)
[a-z&&[hij]]????????? h,i,j中的一個(交集)
\s????????????????????????? 空格字符(空格鍵, tab, 換行, 換頁, 回車)
\S???????????????????????? 非空格字符([^\s])
\d???????????????????????? 一個數字,也就是[0-9]
\D???????????????????????? 一個非數字的字符,也就是[^0-9]
\w??????????????????????? 一個單詞字符(word character),即[a-zA-Z_0-9]
\W???????????????????????一個非單詞的字符,[^\w]
字符類:
B???????????????????????? 字符B
\xhh??????????????????? 16進制值0xhh所表示的字符
\uhhhh??????????????? 16進制值0xhhhh所表示的Unicode字符
\t???????????????????????? Tab
\n??????????????????????? 換行符
\r???????????????????????? 回車符
\f??????????????????????? 換頁符
\e?????????????????????? Escape
邏輯運算符
XY????????????????????? X 后面跟著 Y
X|Y??????????????????? X或Y
(X)?????????????????????一個"要匹配的組(capturing group)". 以后可以用\i來表示第i個被匹配的組。
邊界匹配符
^????????????????????? 一行的開始
$????????????????????? 一行的結尾
\b??????????????????? 一個單詞的邊界
\B??????????????????? 一個非單詞的邊界
\G???????????????????前一個匹配的結束
數量表示符
"數量表示符(quantifier)"的作用是定義模式應該匹配多少個字符。
- Greedy(貪婪的): 除非另有表示,否則數量表示符都是greedy的。Greedy的表達式會一直匹配下去,直到匹配不下去為止。(如果你發現表達式匹配的結果與預期的不符),很有可能是因為,你以為表達式會只匹配前面幾個字符,而實際上它是greedy的,因此會一直匹配下去。
- Reluctant(勉強的): 用問號表示,它會匹配最少的字符。也稱為lazy, minimal matching, non-greedy, 或ungreedy。
- Possessive(占有的): 目前只有Java支持(其它語言都不支持)。它更加先進,所以你可能還不太會用。用正則表達式匹配字符串的時候會產生很多中間狀態,(一般的匹配引擎會保存這種中間狀態,)這樣匹配失敗的時候就能原路返回了。占有型的表達式不保存這種中間狀態,因此也就不會回頭重來了。它能防止正則表達式的失控,同時也能提高運行的效率。
Greedy??????????????????? Reluctant???????????????????????????Possessive????????????????????? 匹配
X???????????????????????????? X??????????????????????????????????????? X?+????????????????????????????????? 匹配一個或零個X
X*??????????????????????????? X*?????????????????????????????????????? X*+????????????????????????????????? 匹配零或多個X
X+?????????????????????????? X+?????????????????????????????????????? X++?????????????????????????????????匹配一個或多個X
X{n}??????????????????????? X{n}??????????????????????????????????? X{n}+?????????????????????????????? 匹配正好n個X
X{n,}?????????????????????? X{n,}??????????????????????????????????X{n,}+??????????????????????????????匹配至少n個X
X{n,m}?????????????????? X{n,m}???????????????????????????????? X{n,m}+?????????????????????????? 匹配至少n個,至多m個X
參考資料
要想進一步學習正則表達式,建議看Mastering Regular Expression, 2nd Edition,作者Jeffrey E. F. Friedl (O’Reilly, 2002)。
"Regular Expressions and the Java Programming Language," Dana Nourie and Mike McCloskey (Sun Microsystems, August 2002) presents a brief overview of java.util.regex, including five illustrative regex-based applications:
http://developer.java.sun.com/developer/technicalArticles/releases/1.4regex/
http://www.tkk7.com/beike/archive/2006/04/28/43832.html?
http://wcjok.bokee.com/4293762.html