一、JDK提供的正則表達式
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();
}
為了實現(xiàn)這個新的CharSequence接口,String,StringBuffer以及CharBuffer都作了修改,很多的正則表達式的操作都要拿CharSequence作參數(shù)。
Matcher類的幾個重要的方法:
- boolean matches():Pattern匹配整個字符串
- boolean lookingAt():Pattern匹配字符串的開頭
- boolean find():發(fā)現(xiàn)CharSequence里的,與pattern相匹配的多個字符序列
boolean find(int start):告訴方法從參數(shù)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的數(shù)目。不包括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數(shù)組,如果帶有l(wèi)imit參數(shù),split( )會限定分割的次數(shù)。
- replaceFirst(String replacement)將字符串里,第一個與模式相匹配的子串替換成replacement。
- replaceAll(String replacement),將輸入字符串里所有與模式相匹配的子串全部替換成replacement。
- appendReplacement(StringBuffer sbuf, String replacement)對sbuf進行逐次替換,而不是像replaceFirst( )或replaceAll( )那樣,只替換第一個或全部子串。這是個非常重要的方法,因為replacement(replaceFirst( )和replaceAll( )只允許用固定的字符串來充當replacement)。有了這個方法,你就可以編程區(qū)分group,從而實現(xiàn)更強大的替換功能。
- 調用完appendReplacement( )之后,為了把剩余的字符串拷貝回去,必須調用appendTail(StringBuffer sbuf, String replacement)。
使用group可以做到逐步縮小匹配的范圍,直至精確匹配的目的。
start( )和end( ):如果匹配成功,start( )會返回此次匹配的開始位置,end( )會返回此次匹配的結束位置,即最后一個字符的下標加一。如果之前的匹配不成功(或者沒匹配),那么無論是調用start( )還是end( ),都會引發(fā)一個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));
}
以上的執(zhí)行結果為name.txt,提取的字符串儲存在m.group(i)中,其中i最大值為m.groupCount();
分割
String regEx="::";
Pattern p=Pattern.compile(regEx);
String[] r=p.split("xd::abc::cde");
執(zhí)行后,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"
三、一個實際的例子
下面的函數(shù)是一個實際應用的例子,需求是需要將抓取的網(wǎng)頁中的<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 一個數(shù)字,也就是[0-9]
\D 一個非數(shù)字的字符,也就是[^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 前一個匹配的結束
數(shù)量表示符
"數(shù)量表示符(quantifier)"的作用是定義模式應該匹配多少個字符。
- Greedy(貪婪的): 除非另有表示,否則數(shù)量表示符都是greedy的。Greedy的表達式會一直匹配下去,直到匹配不下去為止。(如果你發(fā)現(xiàn)表達式匹配的結果與預期的不符),很有可能是因為,你以為表達式會只匹配前面幾個字符,而實際上它是greedy的,因此會一直匹配下去。
- Reluctant(勉強的): 用問號表示,它會匹配最少的字符。也稱為lazy, minimal matching, non-greedy, 或ungreedy。
- Possessive(占有的): 目前只有Java支持(其它語言都不支持)。它更加先進,所以你可能還不太會用。用正則表達式匹配字符串的時候會產(chǎn)生很多中間狀態(tài),(一般的匹配引擎會保存這種中間狀態(tài),)這樣匹配失敗的時候就能原路返回了。占有型的表達式不保存這種中間狀態(tài),因此也就不會回頭重來了。它能防止正則表達式的失控,同時也能提高運行的效率。
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