正则表达式的总结

2009年4月28日星期二

正则表达式的总结

一、JDK提供的正则表达式
Javasjava.util.regex包包括:Pattern类、Matcher类、MatchResult接口和PatternSyntaxException异常:
Pattern对象代表了编译了的正则表达式(Acompiledrepresentationofaregularexpression.)。
Matcher,AnenginethatperformsmatchoperationsonacharactersequencebyinterpretingaPattern.
MatchResult接口:Theresultofamatchoperation.
PatternSyntaxException对象,描述非法的regexpatterns,Uncheckedexceptionthrowntoindicateasyntaxerrorinaregular-expressionpattern.
同时还需要了解是CharSequence,JDK1.4定义的新的接口,它提供了String和StringBuffer这两个类的字符序列的抽象
interfaceCharSequence{charAt(inti);length();subSequence(intstart,intend);toString();}
为了实现这个新的CharSequence接口,String,StringBuffer以及CharBuffer都作了修改,很多的正则表达式的操作都要拿CharSequence作参数。

Matcher类的几个重要的方法:
booleanmatches():Pattern匹配整个字符串
booleanlookingAt():Pattern匹配字符串的开头
booleanfind():发现CharSequence里的,与pattern相匹配的多个字符序列
booleanfind(intstart):告诉方法从参数start位置开始查找
group的概念
Group是指里用括号括起来的,能被后面的表达式调用的正则表达式。
Group0表示整个表达式,group1表示第一个被括起来的group,以此类推。所以;
A(B(C))D
里面有三个group:group0是ABCD,group1是BC,group2是C。
你可以用下述Matcher方法来使用group:
publicintgroupCount()返回matcher对象中的group的数目。不包括group0。
publicStringgroup()返回上次匹配操作(比方说find())的group0(整个匹配)
publicStringgroup(inti)返回上次匹配操作的某个group。如果匹配成功,但是没能找到group,则返回null。
publicintstart(intgroup)返回上次匹配所找到的group的开始位置。
publicintend(intgroup)返回上次匹配所找到的group的结束位置,最后一个字符的下标加一。
split()是指将以正则表达式为界,将字符串分割成String数组,如果带有limit参数,split()会限定分割的次数。
replaceFirst(Stringreplacement)将字符串里,第一个与模式相匹配的子串替换成replacement。
replaceAll(Stringreplacement),将输入字符串里所有与模式相匹配的子串全部替换成replacement。
appendReplacement(StringBuffersbuf,Stringreplacement)对sbuf进行逐次替换,而不是像replaceFirst()或replaceAll()那样,只替换第一个或全部子串。这是个非常重要的方法,因为replacement(replaceFirst()和replaceAll()只允许用固定的字符串来充当replacement)。有了这个方法,你就可以编程区分group,从而实现更强大的替换功能。
调用完appendReplacement()之后,为了把剩余的字符串拷贝回去,必须调用appendTail(StringBuffersbuf,Stringreplacement)。

使用group可以做到逐步缩小匹配的范围,直至精确匹配的目的。
start()和end():如果匹配成功,start()会返回此次匹配的开始位置,end()会返回此次匹配的结束位置,即最后一个字符的下标加一。如果之前的匹配不成功(或者没匹配),那么无论是调用start()还是end(),都会引发一个IllegalStateException。

二、4大使用方法:
查询
Stringstr=abcefgABC;
StringregEx=a|f;//表示a或f
Patternp=Pattern.compile(regEx);
Matcherm=p.matcher(str);
booleanrs=m.find();
如果str中有regEx,那么rs为true,否则为flase。如果想在查找时忽略大小写,则可以写成Patternp=Pattern.compile(regEx,Pattern.CASE_INSENSITIVE);
提取
StringregEx=.+\(.+)$;
Stringstr=c:\dir1\dir2\name.txt;
Patternp=Pattern.compile(regEx);
Matcherm=p.matcher(str);
booleanrs=m.find();
for(inti=1;i=m.groupCount();i++){
System.out.println(m.group(i));
}
以上的执行结果为name.txt,提取的字符串储存在m.group(i)中,其中i最大值为m.groupCount();

分割
StringregEx=::;
Patternp=Pattern.compile(regEx);
String[]r=p.split(xd::abc::cde);
执行后,r就是{xd,abc,cde},其实分割时还有跟简单的方法:
Stringstr=xd::abc::cde;
String[]r=str.split(::);

替换或者删除
StringregEx=a+;//表示一个或多个a
Patternp=Pattern.compile(regEx);
Matcherm=p.matcher(aaabbcedaccdeaa);
Strings=m.replaceAll(A);
结果为AbbcedAccdeA
如果写成空串,既可达到删除的功能,比如:
Strings=m.replaceAll(");
结果为bbcedccde

三、一个实际的例子
下面的函数是一个实际应用的例子,需求是需要将抓取的网页中的imgsrc=http://aa.bb.cc.com/images/**.jpg中的地址,保存到一个地址列表中(以供抓取图片使用),并将绝对地址替换成本地的相对地址,即imgsrc=images/**.jpg。
publicstaticMapString,StringgetImagesURL(Stringdescription){
MapString,Stringmap=newHashMapString,String();
//img的正则表达式:匹配img标签
StringimgPattern=*;
Patternpattern1=Pattern.compile(imgPattern,Pattern.CASE_INSENSITIVE);
Matchermatcher=pattern1.matcher(description);
//imgsrc元素的正则表达式:匹配img标签内的src属性
StringsrcPattern=;
Patternpattern2=Pattern.compile(srcPattern,Pattern.CASE_INSENSITIVE);
while(matcher.find()){
//group()返回符合表达式的内容
Matchermatcher2=pattern2.matcher(matcher.group());
//一定要find(),这是实际的匹配动作
if(matcher2.find()){
Stringsrc=matcher2.group();
log.info(src);
inti2=src.lastIndexOf(/');
inti1=src.indexOf(http);
if(i1!=-1){
map.put(src.substring(i2+1,src.length()-1),src
.substring(i1,src.length()-1));
}
}
}
log.debug(图片:+map);
returnmap;
}

整体思路是先匹配到img标签,然后匹配src属性,并修改src的属性
*的解释:
*:0或多个空格+:至少一个空格
([^]+):指的是到为止的所有的元素,至少一个

常用的正则表达式(参考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一个单词字符(wordcharacter),即[a-zA-Z_0-9]
\W一个非单词的字符,[^\w]
字符类:
B字符B
\xhh16进制值0xhh所表示的字符
\uhhhh16进制值0xhhhh所表示的Unicode字符
\tTab
\n换行符
\r回车符
\f换页符
\eEscape
逻辑运算符
XYX后面跟着Y
X|YX或Y
(X)一个要匹配的组(capturinggroup).以后可以用\i来表示第i个被匹配的组。
边界匹配符
^一行的开始
$一行的结尾
\b一个单词的边界
\B一个非单词的边界
\G前一个匹配的结束

数量表示符
数量表示符(quantifier)的作用是定义模式应该匹配多少个字符。
Greedy(贪婪的):除非另有表示,否则数量表示符都是greedy的。Greedy的表达式会一直匹配下去,直到匹配不下去为止。(如果你发现表达式匹配的结果与预期的不符),很有可能是因为,你以为表达式会只匹配前面几个字符,而实际上它是greedy的,因此会一直匹配下去。
Reluctant(勉强的):用问号表示,它会匹配最少的字符。也称为lazy,minimalmatching,non-greedy,或ungreedy。
Possessive(占有的):目前只有Java支持(其它语言都不支持)。它更加先进,所以你可能还不太会用。用正则表达式匹配字符串的时候会产生很多中间状态,(一般的匹配引擎会保存这种中间状态,)这样匹配失败的时候就能原路返回了。占有型的表达式不保存这种中间状态,因此也就不会回头重来了。它能防止正则表达式的失控,同时也能提高运行的效率。

GreedyReluctantPossessive匹配

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

0 评论:

发表评论