Java正则表达式详解
如果你曾经⽤过Perl或任何其他内建正则表达式⽀持的语⾔,你⼀定知道⽤正则表达式处理⽂本和匹配模式是多么简单。如果你不熟悉这个术语,那么“正则表达式”(Regular Expression)就是⼀个字符构成的串,它定义了⼀个⽤来搜索匹配字符串的模式。
许多语⾔,包括Perl、PHP、Python、JavaScript和JScript,都⽀持⽤正则表达式处理⽂本,⼀些⽂本编辑器⽤正则表达式实现⾼级“搜索-替换”功能。那么Java⼜怎样呢?本⽂写作时,⼀个包含了⽤正则表达式进⾏⽂本处理的Java规范需求(Specification Request)已经得到认可,你可以期待在JDK的下⼀版本中看到它。
然⽽,如果现在就需要使⽤正则表达式,⼜该怎么办呢?你可以从下载源代码开放的Jakarta-ORO库。本⽂接下来的内容先简要地介绍正则表达式的⼊门知识,然后以Jakarta-ORO API为例介绍如何使⽤正则表达式。
⼀、正则表达式基础知识
我们先从简单的开始。假设你要搜索⼀个包含字符“cat”的字符串,搜索⽤的正则表达式就是“cat”。如果搜索对⼤⼩写不敏感,单词“catalog”、“Catherine”、“sophisticated”都可以匹配。也就是说:
1.1 句点符号
假设你在玩英⽂拼字游戏,想要出三个字母的单词,⽽且这些单词必须以“t”字母开头,以“n”字母结束。另外,假设有⼀本英⽂字典,你可以⽤正则表达式搜索它的全部内容。要构造出这个正则表达式,你可以使⽤⼀个通配符——句点符号“.”。这样,完整的表达式就是“t.n”,它匹配“tan”、“ten”、“tin”和“ton”,还匹配“t#n”、“tpn”甚⾄“t n”,还有其他许多⽆意义的组合。这是因为句点符号匹配所有字符,包括空格、Tab字符甚⾄换⾏符:
1.2 ⽅括号符号
为了解决句点符号匹配范围过于⼴泛这⼀问题,你可以在⽅括号(“[]”)⾥⾯指定看来有意义的字符。此时,只有⽅括号⾥⾯指定的字符才参与匹配。也就是说,正则表达式“t[aeio]n”只匹配“tan”、“Ten”、“tin”和“ton”。但“Toon”不匹配,因为在⽅括号之内你只能匹配单个字符:
1.3 “或”符号
如果除了上⾯匹配的所有单词之外,你还想要匹配“toon”,那么,你可以使⽤“|”操作符。“|”操作符的基本意义就是“或”运算。要匹配“toon”,使⽤“t(a|e|i|o|oo)n”正则表达式。这⾥不能使⽤⽅扩号,因为⽅括号只允许匹配单个字符;这⾥必须使⽤圆括号“()”。圆括号还可以⽤来分组,具体请参见后⾯介绍。
1.4 表⽰匹配次数的符号
表⼀显⽰了表⽰匹配次数的符号,这些符号⽤来确定紧靠该符号左边的符号出现的次数:
假设我们要在⽂本⽂件中搜索美国的社会安全号码。这个号码的格式是999-99-9999。⽤来匹配它的正则表达式如图⼀所⽰。在正则表达式中,连字符(“-”)有着特殊的意义,它表⽰⼀个范围,⽐如从0到9。因此,匹配社会安全号码中的连字符号时,它的前⾯要加上⼀个转义字符“\”。
图⼀:匹配所有123-12-1234形式的社会安全号码
假设进⾏搜索的时候,你希望连字符号可以出现,也可以不出现——即,999-99-9999和999999999都属于正确的格式。这时,你可以在连字符号后⾯加上“?”数量限定符号,如图⼆所⽰:
图⼆:匹配所有123-12-1234和123121234形式的社会安全号码
下⾯我们再来看另外⼀个例⼦。美国汽车牌照的⼀种格式是四个数字加上⼆个字母。它的正则表达式前⾯是数字部分“[0-9]{4}”,再加上字母部分“[A-Z] {2}”。图三显⽰了完整的正则表达式。
图三:匹配典型的美国汽车牌照号码,如8836KV
1.5 “否”符号
“^”符号称为“否”符号。如果⽤在⽅括号内,“^”表⽰不想要匹配的字符。例如,图四的正则表达式匹配所有单词,但以“X”字母开头的单词除外。
图四:匹配所有单词,但“X”开头的除外
1.6 圆括号和空⽩符号
假设要从格式为“June 26, 1951”的⽣⽇⽇期中提取出⽉份部分,⽤来匹配该⽇期的正则表达式可以如图五所⽰:
图五:匹配所有Moth DD,YYYY格式的⽇期时间正则表达式java
新出现的“\s”符号是空⽩符号,匹配所有的空⽩字符,包括Tab字符。如果字符串正确匹配,接下来如何提取出⽉份部分呢?只需在⽉份周围加上⼀个圆括号创建⼀个组,然后⽤ORO API(本⽂后⾯详细讨论)提取出它的值。修改后的正则表达式如图六所⽰:
图六:匹配所有Month DD,YYYY格式的⽇期,定义⽉份值为第⼀个组
1.7 其它符号
为简便起见,你可以使⽤⼀些为常见正则表达式创建的快捷符号。如表⼆所⽰:
表⼆:常⽤符号
例如,在前⾯社会安全号码的例⼦中,所有出现“[0-9]”的地⽅我们都可以使⽤“\d”。修改后的正则表达式如图七所⽰:
图七:匹配所有123-12-1234格式的社会安全号码
⼆、Jakar ta-ORO库
有许多源代码开放的正则表达式库可供Java程序员使⽤,⽽且它们中的许多⽀持Perl 5兼容的正则表达式语法。我在这⾥选⽤的是Jakarta-ORO正则表达式库,它是最全⾯的正则表达式API之⼀,⽽且它与Perl 5正则表达式完全兼容。另外,它也是优化得最好的API之⼀。
Jakarta-ORO库以前叫做OROMatcher,Daniel Savarese⼤⽅地把它赠送给了Jakarta Project。你可以按照本⽂最后参考资源的说明下载它。
我⾸先将简要介绍使⽤Jakarta-ORO库时你必须创建和访问的对象,然后介绍如何使⽤Jakarta-ORO API。
▲ PatternCompiler对象
⾸先,创建⼀个Perl5Compiler类的实例,并把它赋值给PatternCompiler接⼝对象。Perl5Compiler是PatternCompiler接⼝的⼀个实现,允许你把正则表达式编译成⽤来匹配的Pattern对象。
▲ Pattern对象
要把正则表达式编译成Pattern对象,调⽤compiler对象的compile()⽅法,并在调⽤参数中指定正则表达式。例如,你可以按照下⾯这种⽅式编译正则表达式“t[aeio]n”:
默认情况下,编译器创建⼀个⼤⼩写敏感的模式(pattern)。因此,上⾯代码编译得到的模式只匹配“tin”、“tan”、 “ten”和“ton”,但不匹
配“Tin”和“taN”。要创建⼀个⼤⼩写不敏感的模式,你应该在调⽤编译器的时候指定⼀个额外的参数:
创建好Pattern对象之后,你就可以通过PatternMatcher类⽤该Pattern对象进⾏模式匹配。
▲ PatternMatcher对象
PatternMatcher对象根据Pattern对象和字符串进⾏匹配检查。你要实例化⼀个Perl5Matcher类并把结果赋值给PatternMatcher接⼝。Perl5Matcher类是PatternMatcher接⼝的⼀个实现,它根据Perl 5正则表达式语法进⾏模式匹配:
使⽤PatternMatcher对象,你可以⽤多个⽅法进⾏匹配操作,这些⽅法的第⼀个参数都是需要根据正则表达式进⾏匹配的字符串:
· boolean matches(String input, Pattern pattern):当输⼊字符串和正则表达式要精确匹配时使⽤。换句话说,正则表达式必须完整地描述输⼊字符串。
· boolean matchesPrefix(String input, Pattern pattern):当正则表达式匹配输⼊字符串起始部分时使⽤。
· boolean contains(String input, Pattern pattern):当正则表达式要匹配输⼊字符串的⼀部分时使⽤(即,它必须是⼀个⼦串)。
另外,在上⾯三个⽅法调⽤中,你还可以⽤PatternMatcherInput对象作为参数替代String对象;这时,你可以从字符串中最后⼀次匹配的位置开始继续进⾏匹配。当字符串可能有多个⼦串匹配给定的正则表达式时,⽤PatternMatcherInput对象作为参数就很有⽤了。⽤PatternMatcherInput对象作为参数替代String时,上述三个⽅法的语法如下:
· boolean matches(PatternMatcherInput input, Pattern pattern)
· boolean matchesPrefix(PatternMatcherInput input, Pattern pattern)
· boolean contains(PatternMatcherInput input, Pattern pattern)
三、应⽤实例
下⾯我们来看看Jakarta-ORO库的⼀些应⽤实例。
3.1 ⽇志⽂件处理
任务:分析⼀个Web服务器⽇志⽂件,确定每⼀个⽤户花在⽹站上的时间。在典型的BEA WebLogic⽇志⽂件中,⽇志记录的格式如下:
分析这个⽇志记录,可以发现,要从这个⽇志⽂件提取的内容有两项:IP地址和页⾯访问时间。你可以⽤分组符号(圆括号)从⽇志记录提取出IP地址和时间标记。
⾸先我们来看看IP地址。IP地址有4个字节构成,每⼀个字节的值在0到255之间,各个字节通过⼀个句点分隔。因此,IP地址中的每⼀个字节有⾄少⼀个、最多三个数字。图⼋显⽰了为IP地址编写的正则表达式:
图⼋:匹配IP地址
IP地址中的句点字符必须进⾏转义处理(前⾯加上“\”),因为IP地址中的句点具有它本来的含义,⽽不是采⽤正则表达式语法中的特殊含义。句点在正则表达式中的特殊含义本⽂前⾯已经介绍。
⽇志记录的时间部分由⼀对⽅括号包围。你可以按照如下思路提取出⽅括号⾥⾯的所有内容:⾸先搜
索起始⽅括号字符(“[”),提取出所有不超过结束⽅括号字符(“]”)的内容,向前寻直⾄到结束⽅括号字符。图九显⽰了这部分的正则表达式。
图九:匹配⾄少⼀个字符,直⾄到“]”
现在,把上述两个正则表达式加上分组符号(圆括号)后合并成单个表达式,这样就可以从⽇志记录提取出IP地址和时间。注意,为了匹配“- -”(但不提取它),正则表达式中间加⼊了“\s-\s-\s”。完整的正则表达式如图⼗所⽰。
图⼗:匹配IP地址和时间标记
现在正则表达式已经编写完毕,接下来可以编写使⽤正则表达式库的Java代码了。
为使⽤Jakarta-ORO库,⾸先创建正则表达式字符串和待分析的⽇志记录字符串:
这⾥使⽤的正则表达式与图⼗的正则表达式差不多完全相同,但有⼀点例外:在Java中,你必须对每⼀个向前的斜杠(“\”)进⾏转义处理。图⼗不是Java的表⽰形式,所以我们要在每个“\”前⾯加上⼀个“\”以免出现编译错误。遗憾的是,转义处理过程很容易出现错误,所以应该⼩⼼谨慎。你可以⾸先输⼊未经转义处理的正则表达式,然后从左到右依次把每⼀个“\”替换成“\\”。如果要复检,你可以试着把它输出到屏幕上。
初始化字符串之后,实例化PatternCompiler对象,⽤PatternCompiler编译正则表达式创建⼀个Pattern对象:
现在,创建PatternMatcher对象,调⽤PatternMatcher接⼝的contain()⽅法检查匹配情况:
接下来,利⽤PatternMatcher接⼝返回的MatchResult对象,输出匹配的组。由于logEntry字符串包含匹配的内容,你可以看到类如下⾯的输出:
3.2 HTML处理实例⼀
下⾯⼀个任务是分析HTML页⾯内FONT标记的所有属性。HTML页⾯内典型的FONT标记如下所⽰:
程序将按照如下形式,输出每⼀个FONT标记的属性:
在这种情况下,我建议你使⽤两个正则表达式。第⼀个如图⼗⼀所⽰,它从字体标记提取出“"face="Arial, Serif" size="+2" color="red"”。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。