6.3.2 正则表达式
在日常开发中处理文本内容时经常会用到正则表达式。通过正则表达式可以简洁地解决一些常见的问题。Java 7对java.util.regex包中的内容进行了更新,主要包括以下几个方面。
1.支持命名捕获分组
捕获分组(capturing group)在使用正则表达式从文本中提取满足某种模式的部分字符时非常有用。通过把感兴趣的字符串的模式封装在捕获分组中,可以在匹配之后很容易地获取这些内容。另外捕获分组也可以在正则表达式中以后向引用(back reference)的方式来直接使用,以表示相同的模式。在Java 7之前,对捕获分组的引用只支持使用表示出现顺序的数字形式。这体现在java.util.Matcher类的group方法只接受int类型作为参数,后向引用的语法也仅支持类似“\1”这样的形式。如果一个正则表达式中包含很多捕获分组,那么开发人员需要清楚每个数字所代表的捕获分组的含义,这对于代码的编写者和阅读者来说都是一件很麻烦的事情。Java 7引入的命名捕获分组可以很好地解决这个问题。通过为每个捕获分组添加一个有意义的名字,使开发人员可以很容易地明白每个分组所表示的含义,这比使用无意义的数字要方便得多。
代码清单6-14给出了通过命名捕获分组来匹配字符串并提取内容时的用法。待匹配的字符串是一个URL,其中通过路径的不同部分来表示查询参数的名称和值。这种采用路径而不是查询字符串来指明参数的方式,在目前的Web开发中比较常见。在正则表达式的模式中,为提取每个参数内容的捕获分组都指定了一个有意义的名字。当匹配完成之后,可以通过Matcher类的group方法来获取每个捕获分组的内容,参数是在模式中指定的名字。采用“?<>”的格式为一个捕获分组命名,“<>”中的内容是名称。名称必须由大小写英文字母和数字组成,同时第一个字符必须是字母。
代码清单6-14 通过命名捕获分组来匹配字符串并提取内容的示例
public void namedCapturingGroup(){
String url="http://www.example.org/uid/alex/docid/1/title/MyFirstBlog";
Pattern pattern=Pattern.compile("^./uid/(?<uid>.)/docid/(?<docid>.)/title/(?<title>.)");
Matcher matcher=pattern.matcher(url);
if(matcher.matches()){
String uid=matcher.group("uid");//值为alex
String docId=matcher.group("docid");//值为1
String title=matcher.group("title");//值为MyFirstBlog
}
}
捕获分组的名称也可以用在正则表达式之中,用来替换使用数字来进行后向引用的做法。代码清单6-15给出了一个示例,用正则表达式来查找重复出现的数字。在正则表达式中引用命名捕获分组使用的语法是“\k<>”,“<>”中是之前定义的捕获分组的名称。
代码清单6-15 捕获分组的名称作为后向引用的示例
public void repeatPattern(){
String str="123-123-12-456-456";
Pattern pattern=Pattern.compile("(?<num>\d+)-\k<num>");
Matcher matcher=pattern.matcher(str);
while(matcher.find()){
String repeat=matcher.group("num");
}
}
2.使用“\x”表示Unicode中的代码点
在正则表达式中,要引用Unicode字符时,可以通过“\u×××”的形式,如“\u0030”表示数字“0”。通过这种方式可以表示某些无法在源代码中直接出现的字符,比如无法输入汉字的开发人员可以通过这种方式来表示中文字符。第4章介绍Unicode时提到过,如果一个Unicode字符不在基本多语言平面(BMP)中,那么要在Java中以代理项对的方式出现,在转换成正则表达式中使用“\u”形式的时候,则需要两个相邻的字符。这样既不够直观,使用起来也不方便。Java 7对正则表达式新增了“\x”来直接表示Unicode中的代码点。“\x”的使用方式与“\u”类似,只不过允许表示的范围更广,如Unicode代码点U+1011F可以直接表示成“\x1011F”。
3.新标记Pattern.UNICODE_CHARACTER_CLASS
在通过Pattern类的compile方法对正则表达式进行编译时,可以指定多个标记。这些标记可以控制匹配时的行为。常见的标记包括设置匹配时不区分大小写的Pattern.CASE_INSENSITIVE,以及设置“.”匹配包括换行符在内的所有字符的Pattern.DOTALL。Java 7中添加了一个额外的标记Pattern.UNICODE_CHARACTER_CLASS来设置使用Unicode版本的预定义字符类和POSIX字符类。以“\d”这个预定义字符类为例,在默认情况下,只会匹配0到9的字符,如果启用了UNICODE_CHARACTER_CLASS标记,“\d”会匹配Unicode规范中所定义的所有属于数字类别的字符,不只是0到9的数字,也会包括其他语言中的数字字符。代码清单6-16给出了一个示例,使用“(\d+)”模式来匹配字符串中的数字。示例中输入的字符串是“100 100”,其中包含了一般的数字“100”和全角数字“100”。在两个Pattern类的对象中,第一个Pattern类的对象在编译时没有使用UNICODE_CHARACTER_CLASS标记,只能匹配一般的数字“100”,而第二个Pattern类对象可以匹配整个字符串。
代码清单6-16 UNICODE_CHARACTER_CLASS标记的使用示例
public void useUnicodeCharacterClass(){
String str="100 100";
Pattern pattern=Pattern.compile("(\d+)");
Matcher matcher=pattern.matcher(str);
if(matcher.find()){
String digit=matcher.group(1);//值为100
}
pattern=Pattern.compile("(\d+)",Pattern.UNICODE_CHARACTER_CLASS);
matcher=pattern.matcher(str);
if(matcher.find()){
String digit=matcher.group(1);//值为100 100
}
}
受到UNICODE_CHARACTER_CLASS标记影响的字符类除了“\d”之外,还包括“\s”、“\w”、“\p{Lower}”、“\p{Upper}”和“\p{Punct}”等。
4.指定Unicode字符使用的书写格式
Java 7中另外一个与正则表达式相关的更新也与Unicode相关。在Java 7之前,正则表达式在匹配Unicode字符串时允许指定Unicode字符所在的区块和类别,而在Java 7中还允许指定Unicode字符使用的书写格式(script)。在指定书写格式时使用的是“\p”,如代码清单6-17所示。“\p{script=Han}”的含义是匹配字符串中书写格式为汉字的Unicode字符,因此正则表达式的执行结果是原始字符串中的汉字“你好”。
代码清单6-17 指定Unicode字符使用的书写格式的示例
public void matchScript(){
String str="abc你好123";
Pattern pattern=Pattern.compile("(\p{script=Han}+)");
Matcher matcher=pattern.matcher(str);
if(matcher.find()){
String hans=matcher.group(1);//值为“你好”
}
}
在匹配时可以使用的合法书写格式名称都定义在枚举类型Character.UnicodeScript中。