java中的正则表达式详解
想必很多人都对正则表达式都头疼。今天,我以我的认识,加上网上一些文章,希望用常人都可以理解的表达方式来和大家分享学习经验。
开篇,还是得说说^和$他们是分别用来匹配字符串的开始和结束,以下分别举例说明:
"^The":开头一定要有"The"字符串;
"ofdespair$":结尾一定要有"ofdespair"的字符串;
那么,
"^abc$":就是要求以abc开头和以abc结尾的字符串,实际上是只有abc匹配。
"notice":匹配包含notice的字符串。
你可以看见如果你没有用我们提到的两个字符(最后一个例子),就是说模式(正则表达式)可以出现在被检验字符串的任何地方,你没有把他锁定到两边。
接着,说说'*','+',和'?',
他们用来表示一个字符可以出现的次数或者顺序.他们分别表示:
"zeroormore"相当于{0,},
"oneormore"相当于{1,},
"zeroorone."相当于{0,1},这里是一些例子:
"ab*":和ab{0,}同义,匹配以a开头,后面可以接0个或者N个b组成的字符串("a","ab","abbb",等);
"ab+":和ab{1,}同义,同上条一样,但最少要有一个b存在("ab","abbb",等.);
"ab?":和ab{0,1}同义,可以没有或者只有一个b;
"a?b+$":匹配以一个或者0个a再加上一个以上的b结尾的字符串.
要点,'*','+',和'?'只管它前面那个字符.
你也可以在大括号里面限制字符出现的个数,比如
"ab{2}":要求a后面一定要跟两个b(一个也不能少)("abb");
"ab{2,}":要求a后面一定要有两个或者两个以上b(如"abb","abbbb",等.);
"ab{3,5}":要求a后面可以有2-5个b("abbb","abbbb",or"abbbbb").
现在我们把一定几个字符放到小括号里,比如:
"a(bc)*":匹配a后面跟0个或者一个"bc";
"a(bc){1,5}":一个到5个"bc."
还有一个字符'│',相当于OR操作:
"hi│hello":匹配含有"hi"或者"hello"的字符串;
"(b│cd)ef":匹配含有"bef"或者"cdef"的字符串;
"(a│b)*c":匹配含有这样多个(包括0个)a或b,后面跟一个c的字符串;
一个点('.')可以代表所有的单一字符,不包括"\n"
如果,要匹配包括"\n"在内的所有单个字符,怎么办?
对了,用'[\n.]'这种模式.
"a.[0-9]":一个a加一个字符再加一个0到9的数字
"^.{3}$":三个任意字符结尾.
中括号括住的内容只匹配一个单一的字符
"[ab]":匹配单个的a或者b(和"a│b"一样);
"[a-d]":匹配'a'到'd'的单个字符(和"a│b│c│d"还有"[abcd]"效果一样);一般我们都用[a-zA-Z]来指定字符为一个大小写英文
"^[a-zA-Z]":匹配以大小写字母开头的字符串
"[0-9]%":匹配含有形如x%的字符串
",[a-zA-Z0-9]$":匹配以逗号再加一个数字或字母结尾的字符串
你也可以把你不想要得字符列在中括号里,你只需要在总括号里面使用'^'作为开头"%[^a-zA-Z]%"匹配含有两个百分号里面有一个非字母的字符串.
要点:^用在中括号开头的时候,就表示排除括号里的字符。为了PHP能够解释,你必须在这些字符面前后加'',并且将一些字符转义.
不要忘记在中括号里面的字符是这条规路的例外?在中括号里面,所有的特殊字符,包括(''),都将失去他们的特殊性质"[*\+?{}.]"匹配含有这些字符的字符串.
还有,正如regx的手册告诉我们:"如果列表里含有']',最好把它作为列表里的第一个字符(可能跟在'^'后面).如果含有'-',最好把它放在最前面或者最后面,or或者一个范围的第二个结束点[a-d-0-9]中间的'-'将有效.
看了上面的例子,你对{n,m}应该理解了吧.要注意的是,n和m都不能为负整数,而且n总是小于m.这样,才能最少匹配n次且最多匹配m次.如"p{1,5}"将匹配"pvpppppp"中的前五个p.
下面说说以\开头的
\b书上说他是用来匹配一个单词边界,就是...比如've\b',可以匹配love里的ve而不匹配very里有ve
\B正好和上面的\b相反.例子我就不举了
.....突然想起来....可以到http://www.phpv.net/article.php/251看看其它用\开头的语法
好,我们来做个应用:
如何构建一个模式来匹配货币数量的输入
构建一个匹配模式去检查输入的信息是否为一个表示money的数字。我们认为一个表示money的数量有四种方式:"10000.00"和"10,000.00",或者没有小数部分,"10000"and"10,000".现在让我们开始构建这个匹配模式:
^[1-9][0-9]*$
这是所变量必须以非0的数字开头.但这也意味着单一的"0"也不能通过测试.以下是解决的方法:
^(0│[1-9][0-9]*)$
"只有0和不以0开头的数字与之匹配",我们也可以允许一个负号在数字之前:
^(0│-?[1-9][0-9]*)$
这就是:"0或者一个以0开头且可能有一个负号在前面的数字."好了,现在让我们别那么严谨,允许以0开头.现在让我们放弃负号,因为我们在表示钱币的时候并不需要用到.我们现在指定模式用来匹配小数部分:
^[0-9]+(\.[0-9]+)?$
这暗示匹配的字符串必须最少以一个阿拉伯数字开头.但是注意,在上面模式中"10."是不匹配的,只有"10"和"10.2"才可以.(你知道为什么吗)^[0-9]+(\.[0-9]{2})?$我们上面指定小数点后面必须有两位小数.如果你认为这样太苛刻,你可以改成:
^[0-9]+(\.[0-9]{1,2})?$
这将允许小数点后面有一到两个字符.现在我们加上用来增加可读性的逗号(每隔三位),我们可以这样表示:
^[0-9]{1,3}(,[0-9]{3})*(\.[0-9]{1,2})?$
不要忘记'+'可以被'*'替代如果你想允许空白字符串被输入话(为什么?).也不要忘记反斜杆'\'在php字符串中可能会出现错误(很普遍的错误).
现在,我们已经可以确认字符串了,我们现在把所有逗号都去掉str_replace(",","",$money)然后在把类型看成double然后我们就可以通过他做数学计算了.
再来一个:
构造检查email的正则表达式
在一个完整的email地址中有三个部分:
1.用户名(在'@'左边的一切),
2.'@',
3.服务器名(就是剩下那部分).
用户名可以含有大小写字母阿拉伯数字,句号('.'),减号('-'),and下划线('_').服务器名字也是符合这个规则,当然下划线除外.
现在,用户名的开始和结束都不能是句点.服务器也是这样.还有你不能有两个连续的句点他们之间至少存在一个字符,好现在我们来看一下怎么为用户名写一个匹配模式:
^[_a-zA-Z0-9-]+$
现在还不能允许句号的存在.我们把它加上:
^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*$
上面的意思就是说:"以至少一个规范字符(除了.)开头,后面跟着0个或者多个以点开始的字符串."
简单化一点,我们可以用eregi()取代ereg().eregi()对大小写不敏感,我们就不需要指定两个范围"a-z"和"A-Z"?只需要指定一个就可以了:
^[_a-z0-9-]+(\.[_a-z0-9-]+)*$
后面的服务器名字也是一样,但要去掉下划线:
^[a-z0-9-]+(\.[a-z0-9-]+)*$
好.现在只需要用"@"把两部分连接:
^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*$
这就是完整的email认证匹配模式了,只需要调用
eregi('^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*$',$eamil)
就可以得到是否为email了.
正则表达式的其他用法
提取字符串
ereg()anderegi()有一个特性是允许用户通过正则表达式去提取字符串的一部分(具体用法你可以阅读手册).比如说,我们想从path/URL提取文件名?下面的代码就是你需要:
ereg("([^\\/]*)$",$pathOrUrl,$regs);
echo$regs[1];
高级的代换
ereg_replace()和eregi_replace()也是非常有用的:假如我们想把所有的间隔负号都替换成逗号:
ereg_replace("[\n\r\t]+",",",trim($str));
最后,我把另一串检查EMAIL的正则表达式让看文章的你来分析一下.
"^[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+'.'@'.'[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'.'[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$"
如果能方便的读懂,那这篇文章的目的就达到了.importjava.io.IOException;
importjava.io.InputStreamReader;
importjava.net.URL;
importjava.util.regex.Matcher;
importjava.util.regex.Pattern;
importjava.util.regex.PatternSyntaxException;/**
*ThisprogramdisplaysallURLsinawebpagebymatchingaregularexpressionthatdescribesthe
*HTMLtag.Starttheprogramas
*javaHrefMatchURL
*@version1.012004-06-04
*@authorCayHorstmann
*/
publicclassHrefMatch
{
publicstaticvoidmain(String[]args)
{
try
{
//getURLstringfromcommandlineorusedefault
StringurlString;
if(args.length>0)urlString=args[0];
elseurlString="http://java.sun.com";//openreaderforURL
InputStreamReaderin=newInputStreamReader(newURL(urlString).openStream());//readcontentsintostringbuilder
StringBuilderinput=newStringBuilder();
intch;
while((ch=in.read())!=-1)
input.append((char)ch);//searchforalloccurrencesofpattern
StringpatternString="])\\s*>";
Patternpattern=Pattern.compile(patternString,Pattern.CASE_INSENSITIVE);
Matchermatcher=pattern.matcher(input);while(matcher.find())
{
intstart=matcher.start();
intend=matcher.end();
Stringmatch=input.substring(start,end);
System.out.println(match);
}
}
catch(IOExceptione)
{
e.printStackTrace();
}
catch(PatternSyntaxExceptione)
{
e.printStackTrace();
}
}
}字符串处理是许多程序中非常重要的一部分,它们可以用于文本显示,数据表示,查找键和很多目的.在Unix下,用户可以使用正则表达式的强健功能实现
这些目的,从Java1.4起,Java核心API就引入了java.util.regex程序包,它是一种有价值的基础工具,可以用于很多类型的文本处理,如匹配,搜索,提取
和分析结构化内容.
java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包。它包括两个类:Pattern和Matcher.
Pattern是一个正则表达式经编译后的表现模式。在java中,通过适当命名的Pattern类可以容易确定String是否匹配某种模式.模式可以象匹配某个特
定的String那样简单,也可以很复杂,需要采用分组和字符类,如空白,数字,字母或控制符.因为Java字符串基于统一字符编码(Unicode),正则表达式也
适用于国际化的应用程序.
Pattern类的方法简述
方法说明
staticPetterncompile(Stringregex,intflag)编译模式,参数regex表示输入的正则表达式,flag表示模式类型(Pattern.CASE_INSENSITIVE表示
不区分大小写)
Matchermatch(CharSequenceinput)获取匹配器,input时输入的待处理的字符串
staticbooleanmatches(Stringregex,CharSequenceinput)快速的匹配调用,直接根据输入的模式regex匹配input
String[]split(CharSequenceinput,intlimit)分隔字符串input,limit参数可以限制分隔的次数
Matcher一个Matcher对象是一个状态机器,它依据Pattern对象做为匹配模式对字符串展开匹配检查。首先一个Pattern实例订制了一个所用语法与
PERL的类似的正则表达式经编译后的模式,然后一个Matcher实例在这个给定的Pattern实例的模式控制下进行字符串的匹配工作。
Matcher类的方法简述
方法说明
booleanmatches()对整个输入字符串进行模式匹配.
booleanlookingAt()从输入字符串的开始处进行模式匹配
booleanfind(intstart)从start处开始匹配模式
intgroupCount()返回匹配后的分组数目
StringreplaceAll(Stringreplacement)用给定的replacement全部替代匹配的部分
StringrepalceFirst(Stringreplacement)用给定的replacement替代第一次匹配的部分
MatcherappendReplacement(StringBuffersb,Stringreplacement)根据模式用replacement替换相应内容,并将匹配的结果添加到sb当前位置之后
StringBufferappendTail(StringBuffersb)将输入序列中匹配之后的末尾字串添加到sb当前位置之后.
正则表达式中常见通配符:
对于单字符串比较而言,使用正则表达式没有什么优势.Regex的真正强大之处在于体现在包括字符类和量词(*,+,?)的更复杂的模式上.
字符类包括:
\d数字
\D非数字
\w单字字符(0-9,A-Z,a-z)
\W非单字字符
\s空白(空格符,换行符,回车符,制表符)
\S非空白
[]由方括号内的一个字符列表创建的自定义字符类
.匹配任何单个字符
下面的字符将用于控制将一个子模式应用到匹配次数的过程.
?重复前面的子模式0次到一次
*重复前面的子模式0次或多次
+重复前面的子模式一次到多次
以下是实例部分:
实例一:
正则式是最简单的能准确匹配一个给定String的模式,模式与要匹配的文本是等价的.静态的Pattern.matches方法用于比较一个String是否匹配一个给
定模式.例程如下:
Stringdata="java";
booleanresult=Pattern.matches("java",data);
实例二:
String[]dataArr={"moon","mon","moon","mono"};
for(Stringstr:dataArr){
StringpatternStr="m(o+)n";
booleanresult=Pattern.matches(patternStr,str);
if(result){
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
模式是"m(o+)n",它表示mn中间的o可以重复一次或多次,因此moon,mon,mooon能匹配成功,而mono在n后多了一个o,和模式匹配不上.
注:
+表示一次或多次;?表示0次或一次;*表示0次或多次.
实例三:
String[]dataArr={"ban","ben","bin","bon","bun","byn","baen"};
for(Stringstr:dataArr){
StringpatternStr="b[aeiou]n";
booleanresult=Pattern.matches(patternStr,str);
if(result){
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
注:方括号中只允许的单个字符,模式"b[aeiou]n"指定,只有以b开头,n结尾,中间是a,e,i,o,u中任意一个的才能匹配上,所以数组的前五个可以匹配,
后两个元素无法匹配.
方括号[]表示只有其中指定的字符才能匹配.
实例四:
String[]dataArr={"been","bean","boon","buin","bynn"};
for(Stringstr:dataArr){
StringpatternStr="b(ee|ea|oo)n";
booleanresult=Pattern.matches(patternStr,str);
if(result){
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
如果需要匹配多个字符,那么[]就不能用上了,这里我们可以用()加上|来代替,()表示一组,|表示或的关系,模式b(ee|ea|oo)n就能匹配been,bean,boon
等.
因此前三个能匹配上,而后两个不能.
实例五:
String[]dataArr={"1","10","101","1010","100+"};
for(Stringstr:dataArr){
StringpatternStr="\\d+";
booleanresult=Pattern.matches(patternStr,str);
if(result){
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
注:从前面可以知道,\\d表示的是数字,而+表示一次或多次,所以模式\\d+就表示一位或多位数字.
因此前四个能匹配上,最后一个因为+号是非数字字符而匹配不上.
实例六:
String[]dataArr={"a100","b20","c30","df10000","gh0t"};
for(Stringstr:dataArr){
StringpatternStr="\\w+\\d+";
booleanresult=Pattern.matches(patternStr,str);
if(result){
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
模式\\w+\\d+表示的是以多个单字字符开头,多个数字结尾的字符串,因此前四个能匹配上,最后一个因为数字后还含有单字字符而不能匹配.
实例七:
Stringstr="薪水,职位姓名;年龄性别";
String[]dataArr=str.split("[,\\s;]");
for(StringstrTmp:dataArr){
System.out.println(strTmp);
}
String类的split函数支持正则表达式,上例中模式能匹配",",单个空格,";"中的一个,split函数能把它们中任意一个当作分隔符,将一个字符串劈
分成字符串数组.
实例八:
Stringstr="2007年12月11日";
Patternp=Pattern.compile("[年月日]");
String[]dataArr=p.split(str);
for(StringstrTmp:dataArr){
System.out.println(strTmp);
}
Pattern是一个正则表达式经编译后的表现模式,它的split方法能有效劈分字符串.
注意其和String.split()使用上的不同.
实例九:
Stringstr="10元1000人民币10000元100000RMB";
str=str.replaceAll("(\\d+)(元|人民币|RMB)","$1¥");
System.out.println(str);
上例中,模式"(\\d+)(元|人民币|RMB)"按括号分成了两组,第一组\\d+匹配单个或多个数字,第二组匹配元,人民币,RMB中的任意一个,替换部分$1表
示第一个组匹配的部分不变,其余组替换成¥.
替换后的str为¥10¥1000¥10000¥100000
实例十:
Patternp=Pattern.compile("m(o+)n",Pattern.CASE_INSENSITIVE);
//用Pattern类的matcher()方法生成一个Matcher对象
Matcherm=p.matcher("moonmooonMonmooooonMooon");
StringBuffersb=newStringBuffer();
//使用find()方法查找第一个匹配的对象
booleanresult=m.find();
//使用循环找出模式匹配的内容替换之,再将内容加到sb里
while(result){
m.appendReplacement(sb,"moon");
result=m.find();
}
//最后调用appendTail()方法将最后一次匹配后的剩余字符串加到sb里;
m.appendTail(sb);
System.out.println("替换后内容是"+sb.toString());
实例十一:
除了用+表示一次或多次,*表示0次或多次,?表示0次或一次外,还可以用{}来指定精确指定出现的次数,X{2,5}表示X最少出现2次,最多出现5次;X{2,}表
示X最少出现2次,多则不限;X{5}表示X只精确的出现5次.
例程:
String[]dataArr={"google","gooogle","gooooogle","goooooogle","ggle"};
for(Stringstr:dataArr){
StringpatternStr="g(o{2,5})gle";
booleanresult=Pattern.matches(patternStr,str);
if(result){
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
实例十二:
-表示从..到…,如[a-e]等同于[abcde]
String[]dataArr={"Tan","Tbn","Tcn","Ton","Twn"};
for(Stringstr:dataArr){
Stringregex="T[a-c]n";
booleanresult=Pattern.matches(regex,str);
if(result){
System.out.println("字符串"+str+"匹配模式"+regex+"成功");
}else{
System.out.println("字符串"+str+"匹配模式"+regex+"失败");
}
}
实例十三:不区分大小写匹配.
正则表达式默认都是区分大小写的,使用了Pattern.CASE_INSENSITIVE则不对大小写进行区分.
StringpatternStr="ab";
Patternpattern=Pattern.compile(patternStr,Pattern.CASE_INSENSITIVE);
String[]dataArr={"ab","Ab","AB"};
for(Stringstr:dataArr){
Matchermatcher=pattern.matcher(str);
if(matcher.find()){
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
}
实例十四:使用正则表达式劈分字符串.
注意这里要把复杂的模式写在前面,否则简单模式会先匹配上.
Stringinput="职务=GM薪水=50000,姓名=职业经理人;性别=男年龄=45";
StringpatternStr="(\\s*,\\s*)|(\\s*;\\s*)|(\\s+)";
Patternpattern=Pattern.compile(patternStr);
String[]dataArr=pattern.split(input);
for(Stringstr:dataArr){
System.out.println(str);
}
实例十五:解析正则表达式中的文字,\\1对应第一个小括号括起来的group1.
Stringregex="(\\w+)";
Patternpattern=Pattern.compile(regex);
Stringinput="Bill50000GM";
Matchermatcher=pattern.matcher(input);
while(matcher.find()){
System.out.println(matcher.group(2));
}
实例十六:将单词数字混合的字符串的单词部分大写.
Stringregex="([a-zA-Z]+[0-9]+)";
Patternpattern=Pattern.compile(regex);
Stringinput="age45salary50000050000title";
Matchermatcher=pattern.matcher(input);
StringBuffersb=newStringBuffer();
while(matcher.find()){
Stringreplacement=matcher.group(1).toUpperCase();
matcher.appendReplacement(sb,replacement);
}
matcher.appendTail(sb);
System.out.println("替换完的字串为"+sb.toString());importjava.util.regex.Matcher;
importjava.util.regex.Pattern;/**
*@authorZhuYuanXi
*正则表达式的使用
*/
publicclassTest{
/**
*字符串匹配
*@paramexpression正则表达式字符串
*@paramtext要进行匹配的字符串
*/
privatestaticvoidmatchingText(Stringexpression,Stringtext){
Patternp=Pattern.compile(expression);//正则表达式
Matcherm=p.matcher(text);//操作的字符串
booleanb=m.matches();
System.out.println(b);
}
/**
*字符串替换
*@paramexpression正则表达式字符串
*@paramtext要进行替换操作的字符串
*@paramstr要替换的字符串
*/
privatestaticvoidreplaceText(Stringexpression,Stringtext,Stringstr){
Patternp=Pattern.compile(expression);//正则表达式
Matcherm=p.matcher(text);//操作的字符串
Strings=m.replaceAll(str);
System.out.println(s);
}
/**
*字符串查找
*@paramexpression正则表达式字符串
*@paramtext要进行查找操作的字符串
*@paramstr要查找的字符串
*/
privatestaticvoidfindText(Stringexpression,Stringtext,Stringstr){
Patternp=Pattern.compile(expression);//正则表达式
Matcherm=p.matcher(text);//操作的字符串
StringBuffersb=newStringBuffer();
inti=0;
while(m.find()){
m.appendReplacement(sb,str);
i++;
}
m.appendTail(sb);
System.out.println(sb.toString());
System.out.println(i);
}
/**
*字符串分割
*@paramexpression正则表达式字符串
*@paramtext要进行分割操作的字符串
*/
privatestaticvoidsplitText(Stringexpression,Stringtext){
Patternp=Pattern.compile(expression);//正则表达式
String[]a=p.split(text);
for(inti=0;i正则表达式用来指定字符串模式。当你需要定位匹配某种模式的字符串时就可以使用正则表达式。例如,我们下面的一个例程就是在一个HTML文件中通过查找字符串模式来定位所有的超链接。
当然,为了指定一种模式,使用...这种记号是不够精确的。你需要精确地指定什么样的字符
排列是一个合法的匹配。当描述某种模式时,你需要使用一种特殊的语法。
这里有一个简单例子。正则表达式
[Jj]ava.+
匹配下列形式的任何字符串: