黑马程序员-Java基础、正则表达式
正则表达式是一个强大的字符串处理工具,可以对字符串进行查找、提取、分割、替换等操作。String类里也提供了几个特殊的方法:
1.boolean matches(String regex):判断该字符串是否匹配指定正则表达式。
2.String replaceAll(String regex,String replacement):返回该字符串中所有匹配正则表达式的子串替换成replacement后的新字符串。
3.String replaceFirst(String regex,String replacement):返回该字符串中第一个匹配正则表达式的子串替换成replacement后的新字符串。
4.String[] split(String regex):根据给定正则表达式拆分该字符串后得到的字符串数组。
上面这些特殊的方法都依赖于Java提供的正则表达式的支持,除此之外,Java提供了Pattern和Matcher两个类专门用于提供正则表达式支持。
创建正则表达式:
实际上正则表达式就是一个字符串模版,可以匹配一批字符串,所以创建正则表达式就是创建一个特殊的字符串,正则表达式可以使用的合法字符如下表所示:
除此之外,正则表达式中有一些特殊字符,这些特殊字符在正则表达式中有其特殊的用途,如前面介绍的反斜线(\),如果需要匹配这些特殊字符,必须首先将这些字符转义,也就是在前面添加一个反斜线(\).正则表达式中的特殊字符如下表所示:
如果使用上面多个字符拼起来,就可以创建一个正则表达式,例如:”\\u0041\\” //匹配a\ ”\\0101\t” //匹配a<制表符> ”\?\[” //匹配?[ 这些正则表达式依然只能匹配单个字符串,这是因为还未在正则表达式中使用“通配符”,“通配符”是可以匹配多个字符的特殊字符。正则表达式中“通配符”远远超出了普通通配符的功能,它被称为预定义字符,正则表达式支持如下表所示的预定义字符:
有了上面的预定义字符后,我们就可以创建更加强大的正则表达式,例如:
c\wt //可以匹配cat、cbt、cct、c0t、等一批字符串。
\d\d\d-\d\d\d-\d\d\d\d //可以匹配如000-000-0000形式的电话号码。
在一些特殊情况,例如我们只想匹配a-f的字母,或者想匹配除了ab之外的所有小写字母,或者相匹配中文字符,上面预定义字符就无能为力了,此处需要使用方括号表达式,方括号表达式有如下表所示的几种形式:
正则表达式还支持圆括号表达式,它用于将多个表达式组成一个子表达式,圆括号中可以使用或运算符(|),例如正则表达式”(public|protected|private)”用于匹配Java三个访问控制符其中之一。
除此之外,Java正则表达式还支持如下表所示几个边界匹配符:
前面例子中需要建立一个匹配000-000-0000形式的电话号码时,使用的正则表达式看起来比较繁琐。实际上,正则表达式还提供了数量标识符,正则表达式支持的数量标识符有如下几种模式:
1.Greedy(贪婪模式):除非另有表示,数量表示符默认采用贪婪模式。贪婪模式的表达式会一直匹配下去,直到无法匹配为止。如果你发现表达式匹配的结果与预期不符,很可能是因为,你以为表达式会只匹配前面的几个字符,而实际上他是贪婪模式,所以会一直匹配下去。
2.Reluctant(勉强模式):用问号后缀(?)表示,他只会匹配最少的字符。也称为最小匹配模式。
3.Possessive(占有模式):用加号后缀(+)表示,目前只有Java支持占有模式,通常比较少用。
三种模式的数量表示符如下表所示:
关于贪婪模式和勉强模式的对比看如下代码:
1 package com.king; 2 3 4 5 public class TestEasy { 6 7 8 9 /** 10 11 * @author 王者黑桃 12 13 */ 14 15 public static void main(String[] args) { 16 17 String str="hello , world !"; 18 19 // 贪婪模式的正则表达式,将输出:0 , world ! 20 21 System.out.println(str.replaceFirst("\\w*", "0")); 22 23 // 勉强模式的正则表达式,将输出:0hello , world ! 24 25 System.out.println(str.replaceFirst("\\w*?", "0")); 26 27 } 28 29 30 31 }
使用正则表达式:
一旦在程序中定义正则表达式之后,就可以使用Pattern和Matcher来使用正则表达式。
Pattern对象是正则表达式编译后在内存中的表示形式,因此,正则表达式字符串必须先被编译为Pattern对象,然后再利用该Pattern对象创建对应的Matcher对象。执行匹配所涉及的状态保留在Matcher对象中,多个Matcher对象可共享同一个Pattern对象。
因此,典型的调用顺序如下代码片段:
1 //将一个字符串编译成Pattern对象 2 3 Pattern p=Pattern.compile("a*b"); 4 5 //使用Pattern对象创建Matcher对象 6 7 Matcher m=p.matcher("aaaaab"); 8 9 //返回true 10 11 boolean b=m.matches();
上面定义的Pattern对象可以多次重复使用。如果某个正则表达式仅需一次使用,可直接使用Pattern类的静态matches方法,此方法自动把指定字符串编译成匿名的Pattern对象,并执行匹配,如下代码片段:
1 //返回true 2 3 boolean bool=Pattern.matches("a*b", "aaaab");
上面语句等效与前面的三个语句。但采用这种语句每次都需要重新编译新的Pattern对象,不能重复利用已编译的Pattern对象,所以效率不高。
Pattern是不可变类,可供多个并发线程安全使用。
在Pattern类和Matcher类中看到一个CharSequence接口,该接口代表一个字符序列,其中CharBuffer、String、StringBuffer、StringBuilder都是它的实现类。简单的说,CharSequence代表各种表示形式的字符串。
Matcher类提供了如下几种常用方法:
1.find:返回目标字符串中是否包含与Pattern匹配的子串。
2.group:返回上一次与Pattern匹配的子串。
3.start:返回上一次与Pattern匹配的子串在目标字符串中的开始位置。
4.end:返回上一次与Pattern匹配的子串在目标字符串中的结束位置+1。
5.lookingAt:返回目标字符串前面部分与Pattern是否匹配。
6.matches:返回整个目标字符串与Pattern是否匹配。
7.reset:可以将现有的Matcher对象应用于一个新的字符串序列。
通过Matcher类的find和group方法可以从目标字符串中依次取出特定子串(匹配正则表达式的子串),下面例子程序示范了这种用途:
1 package com.king; 2 3 4 5 import java.util.regex.Matcher; 6 7 import java.util.regex.Pattern; 8 9 10 11 public class FindGroup { 12 13 14 15 /** 16 17 * @author 王者黑桃 18 19 */ 20 21 public static void main(String[] args) { 22 23 // 创建一个Pattern对象,并用它建立一个Matcher对象 24 25 Matcher m=Pattern.compile("\\w+") 26 27 .matcher("I like java very much !"); 28 29 while(m.find()){ 30 31 System.out.println(m.group()); 32 33 } 34 35 System.out.println("================"); 36 37 int i=0; 38 39 while(m.find(i)){ 40 41 System.out.print(m.group()+"\t"); 42 43 i++; 44 45 } 46 47 } 48 49 50 51 }
运行上面程序将看到如下结果:
1 I 2 3 like 4 5 java 6 7 very 8 9 much 10 11 ================ 12 13 I like like ike ke e java java ava va a very very ery ry y much much uch ch h
从上面运行结果可以看出,find方法依次查找字符串中与Pattern匹配的子串,一旦找到对应的子串,下次调用find方法将接着向下查找。除此之外,find方法还可以传入一个int类型的参数,带int参数的find方法从该int索引处向下搜索。
Start和end方法主要用于确定子串在目标字符串中的位置,如下程序代码所示:
1 package com.king; 2 3 4 5 import java.util.regex.Matcher; 6 7 import java.util.regex.Pattern; 8 9 10 11 public class StartEnd { 12 13 14 15 /** 16 17 * @author 王者黑桃 18 19 */ 20 21 public static void main(String[] args) { 22 23 // 创建一个Pattern对象,并用它建立一个Matcher对象 24 25 String string="I like java very much !"; 26 27 System.out.println("目标字符串是:"+string); 28 29 Matcher mt=Pattern.compile("\\w+") 30 31 .matcher(string); 32 33 while(mt.find()){ 34 35 System.out.println(mt.group()+"子串的起始位置:" 36 37 +mt.start()+",子串的结束位置:" 38 39 +mt.end()); 40 41 } 42 43 } 44 45 46 47 }
上面程序使用find、group逐项取出目标字符串中与指定正则表达式匹配的子串,并使用start、end方法来返回子串在目标字符串中的位置,运行结果:
1 目标字符串是:I like java very much ! 2 3 I子串的起始位置:0,子串的结束位置:1 4 5 like子串的起始位置:2,子串的结束位置:6 6 7 java子串的起始位置:7,子串的结束位置:11 8 9 very子串的起始位置:12,子串的结束位置:16 10 11 much子串的起始位置:17,子串的结束位置:21
Matches和lookingAt方法有点类似,只是matches方法要求整个字符串和Pattern完全匹配时才返回true,而lookingAt只要字符串以Pattern开头就返回true。reset方法可将现有的Matcher对象应用于新的字符串序列。看如下例子程序:
1 package com.king; 2 3 4 5 import java.util.regex.Matcher; 6 7 import java.util.regex.Pattern; 8 9 10 11 public class TestMatches { 12 13 14 15 /** 16 17 * @author 王者黑桃 18 19 */ 20 21 public static void main(String[] args) { 22 23 String[] mails={ 24 25 "18338878315@163.com", 26 27 "614508584@qq.com", 28 29 "jiangyantao@abc.xx", 30 31 "jiang1016@live.cn" 32 33 }; 34 35 String mailRegEx="\\w{3,20}@\\w+\\.(com|org|cn|gov|net)"; 36 37 Pattern mailPattern=Pattern.compile(mailRegEx); 38 39 Matcher mailMatcher=null; 40 41 for(String mail:mails){ 42 43 if(mailMatcher==null){ 44 45 mailMatcher=mailPattern.matcher(mail); 46 47 }else{ 48 49 mailMatcher.reset(mail); 50 51 } 52 53 if(mailMatcher.matches()){ 54 55 System.out.println(mail+"是一个有效的邮件地址!"); 56 57 } 58 59 else{ 60 61 System.out.println(mail+"不是一个有效的邮件地址!"); 62 63 } 64 65 } 66 67 } 68 69 70 71 }
输出结果为:
18338878315@163.com是一个有效的邮件地址! 614508584@qq.com是一个有效的邮件地址! jiangyantao@abc.xx不是一个有效的邮件地址! jiang1016@live.cn是一个有效的邮件地址!
上面程序创建了一个邮件地址的Pattern,接着用这个Pattern与多个邮件地址进行匹配。当程序中的Matcher为NULL时,程序调用matcher方法来创建一个Matcher对象,一旦Matcher对象被创建以后,程序调用Matcher的reset方法将该Matcher对象应用与新的字符序列。
事实上,String类里也提供了matches方法,该方法返回该字符串是否匹配指定正则表达式。如:
1 "18338878315@163.com".matches("\\w{3,20}@\\w+\\.(com|org|cn|gov|net)"); //返回true
除此之外,还可利用正则表达式对目标字符串进行分割、查找、替换等操作,看如下程序代码:
1 package com.king; 2 3 4 5 import java.util.Arrays; 6 7 import java.util.regex.Matcher; 8 9 import java.util.regex.Pattern; 10 11 12 13 public class TestReplace { 14 15 16 17 /** 18 19 * @author 王者黑桃 20 21 */ 22 23 public static void main(String[] args) { 24 25 String[] msgs={ 26 27 "I like java very much", 28 29 "and I want to join in DarkHorse Programmer", 30 31 "I hope it will be succeed " 32 33 }; 34 35 for(String msg:msgs){ 36 37 System.out.println(msg.replaceFirst("an\\w*", "黑桃K")); 38 39 System.out.println(Arrays.toString(msg.split(" "))); 40 41 } 42 43 System.out.println("================"); 44 45 Pattern msgPattern=Pattern.compile("an\\w*"); 46 47 Matcher msgMatcher=null; 48 49 for(int i=0;i<msgs.length;i++){ 50 51 if(msgMatcher==null){ 52 53 msgMatcher=msgPattern.matcher(msgs[i]); 54 55 }else{ 56 57 msgMatcher.reset(msgs[i]); 58 59 } 60 61 System.out.println(msgMatcher.replaceAll("黑桃K")); 62 63 } 64 65 } 66 67 68 69 }
输出结果为:
I like java very much [I, like, java, very, much] 黑桃K I want to join in DarkHorse Programmer [and, I, want, to, join, in, DarkHorse, Programmer] I hope it will be succeed [I, hope, it, will, be, succeed] ================ I like java very much 黑桃K I w黑桃K to join in DarkHorse Programmer I hope it will be succeed
正则表达式是一个功能非常灵活的文本处理工具,增加了正则表达式支持后的Java,可以不再需要StringTokenizer类(也是一个处理字符串的工具,但功能远不如正则表达式强大)的帮助就可以进行复杂的字符串处理。