快捷搜索:

如何使用Java自带的正则表达式

在Sun的Java JDK 1.40版本中,Java自带了支持正则表达式的包,本文就抛砖引玉地先容了若何应用java.util.regex包。

可粗略预计一下,除了有时用Linux的外,其他Linu x用户都邑碰到正则表达式。正则表达式是个极度强大年夜对象,而且在字符串模式-匹配和字符串模式-调换方面富有弹性。在Unix天下里,正则表达式险些没有什么限定,可肯定的是,它利用异常之广泛。

正则表达式的引擎已被许多通俗的Unix对象所实现,包括grep,awk,vi和Emacs等。此外,许多应用对照广泛的脚本说话也支持正则表达式,比如Python,Tcl,JavaScript,以及最闻名的Perl。

我很早曩昔便是个Perl方面的黑客,假如你和我一样话,你也会异常依附你手边的这些强大年夜的text-munging对象。近几年来,像其他法度榜样开拓者一样,我也越来越关注Java的开拓。

Java作为一种开拓说话,有许多值得保举的地方,然则它不停以来没有自带对正则表达式的支持。直到近来,借助于第三方的类库,Java开始支持正则表达式,但这些第三方的类库都不同等、兼容性差,而且掩护代码起来很糟糕。这个毛病,对我选择Java作为重要的开拓对象来说,不停是个伟大年夜的挂念之处。

你可以想象,当我知道Sun的Java JDK 1.40版本包孕了java.util.regex(一个完全开放、自带的正则表达式包)时,是多么的痛快!很搞笑的说,我花好些光阴去掘客这个被暗藏起来的宝石。我异常惊奇的是,Java这样的一个很大年夜改进(自带了java.util.regex包)为什么不多公开一点呢?!

近来,Java双脚都跳进了正则表达式的天下。java.util.regex包在支持正则表达也有它的过人之处,别的Java也供给具体的相关阐明文档。使得朦朦胧胧的regex神秘天气也逐步被拨开。有一些正则表达式的构成(可能最显明的是,在于糅合了字符类库)在Perl都找不到。

在regex包中,包括了两个类,Pattern(模式类)和Matcher(匹配器类)。Pattern类是用来表达和述说所要搜索模式的工具,Matcher类是真正影响搜索的工具。另加一个新的例外类,PatternSyntaxException,当碰到分歧法的搜索模式时,会抛出例外。

纵然对正则表达式很认识,你会发明,经由过程java应用正则表达式也相称简单。要阐明的一点是,对那些被Perl的单行匹配所宠坏的Perl狂热喜欢者来说,在应用java的regex包进行调换操作时,会比他们所曩昔常用的措施费事些。

本文的局限之处,它不是一篇正则表达式用法的完全教程。假如读者要对正则表达进一步懂得的话,保举涉猎Jeffrey Frieldl的Mastering Regular Expressions,该书由O’Reilly出版社出版。我下面就举一些例子来教读者若何应用正则表达式,以及若何更简单地去应用它。

设计一个简单的表达式来匹配任何电话号码数字可能是对照繁杂的工作,缘故原由在于电话号码款式有很多种环境。所有必须选择一个对照有效的模式。比如:(212) 555-1212, 212-555-1212和212 555 1212,某些人会觉得它们都是等价的。

首先让我们构成一个正则表达式。为简单起见,先构成一个正则表达式来识别下面款式的电话号码数字:(nnn)nnn-nnnn。

第一步,创建一个pattern工具来匹配上面的子字符串。一旦法度榜样运行后,假如必要的话,可以让这个工具一样平常化。匹配上面款式的正则表达可以这样构成:(\d{3})\s\d{3}-\d{4},此中\d单字符类型用来匹配从0到9的任何数字,别的{3}重复符号,是个简便的暗号,用来表示有3个继续的数字位,也等效于(\d\d\d)。\s也别的一个对照有用的单字符类型,用来匹配空格,比如Space键,tab键和换行符。

是不是很简单?然则,假如把这个正则表达式的模式用在java法度榜样中,还要做两件事。对java的说冥器来说,在反斜线字符(\)前的字符有特殊的含义。在java中,与regex有关的包,并不都能理解和识别反斜线字符(\),只管可以碰命运运限。但为避免这一点,即为了让反斜线字符(\)在模式工具中被完全地通报,应该用双反斜线字符(\)。此外圆括号在正则表达中两层含义,假如想让它解释为字面上意思(即圆括号),也必要在它前面用双反斜线字符(\)。也便是像下面的一样:

\\(\\d{3}\\)\\s\\d{3}-\\d{4}

现在先容如何在java代码中实现刚才所讲的正则表达式。要记着的事,在用正则表达式的包时,在你所定义的类前必要包孕该包,也便是这样的一行:

import java.util.regex.*;

下面的一段代码实现的功能是,从一个文本文件逐行读入,并逐行搜索电话号码数字,一旦找到所匹配的,然后输出在节制台。

BufferedReader in;

Pattern pattern = Pattern.compile("\\(\\d{3}\\)\\s\\d{3}-\\d{4}");

in = new BufferedReader(new FileReader("phone"));

String s;

while ((s = in.readLine()) != null)

{

Matcher matcher = pattern.matcher(s);

if (matcher.find())

{

System.out.println(matcher.group());

}

}

in.close();

对那些认识用Python或Javascript来实现正则表达式的人来说,这段代码很寻常。在Python和Javascript这些说话中,或者其他的说话,这些正则表达式一旦明确地编译过后,你想用到哪里都可以。与Perl的单步匹配比拟,看起来多多做了些事情,但这并不很费事。

find()措施,就像你所想象的,用来搜索与正则表达式相匹配的任何目标字符串,group()措施,用来返回包孕了所匹配文本的字符串。应留意的是,上面的代码,仅用在每行只能含有一个匹配的电话号码数字字符串时。可以肯定的说,java的正则表达式包能用在一行含有多个匹配目标时的搜索。本文的原意在于举一些简单的例子来激起读者进一步去进修java自带的正则表达式包,以是对此就没有进行深入的探究。

这相称漂亮吧! 然则很遗憾的是,这仅是个电话号码匹配器。很显着,还有两点可以改进。假如在电话号码的开首,即区位号和本地号码之间可能会有空格。我们也可匹配这些环境,则经由过程在正则表达式中加入\s?来实现,此中?元字符表示在模式可能有0或1个空格符。

第二点是,在本地号码位的前三位和后四位数字间有可能是空格符,而不是连字号,更有胜者,或根本就没有分隔符,便是7位数字连在一路。对这几种环境,我们可以用(-|)?来办理。这个布局的正则表达式便是转换器,它能匹配上面所说的几种环境。在()能含有管道符|时,它能匹配是否含有空格符或连字符,而尾部的?元字符表示是否根本没有分隔符的环境。

着末,区位号也可能没有包孕在圆括号内,对此可以简单地在圆括号后附上?元字符,但这不是一个很好的办理措施。由于它也包孕了不配对的圆括号,比如"(555" 或 "555)"。相反,我们可以经由过程另一种转换器来逼迫让电话号码是否带有有圆括号:(\(\d{3}\)|\d{3})。假如我们把上面代码中的正则表达式用这些改进后的来调换的话,上面的代码就成了一个异常有用的电话号码数字匹配器:

Pattern pattern =

Pattern.compile("(\\(\\d{3}\\)|\\d{3})\\s?\\d{3}(-|)?\\d{4}");

可以确定的是,你可以自己试着进一步改进上面的代码。

现在看看第二个例子,它是从Friedl的中改编过来的。其功能是用来反省文本文件中是否有重复的单词,这在印刷排版中会常常碰到,同样也是个语法反省器的问题。

匹配单词,像其他的一样,也可以经由过程好几种的正则表达式来完成。可能最直接的是\b\w+\b,其优点在于只需用少量的regex元字符。此中\w元字符用来匹配从字母a到u的任何字符。+元字符表示匹配匹配一次或多次字符,\b元字符是用来阐明匹配单词的界限,它可所以空格或任何一种不合的标点符号(包括逗号,句号等)。

现在,我们如何来反省一个给定的单词是否被重复了三次?为完成这个义务,需充分使用正则表达式中的所熟知的向后扫描。如前面提到的,圆括号在正则表达式中有几种不合的用法,一个便是能供给组合类型,组合类型用来保存所匹配的结果或部分匹配的结果(以便后面能用到),纵然碰到有相同的模式。在同样的正则表达中,可能(也平日期望)不止有一个组合类型。在第n个组合类型中匹配结果可以经由过程向后扫描来获取到。向后扫描使得搜索重复的单词异常简单:\b(\w+)\s+\1\b。

圆括号形成了一个组合类型,在这个正则表示中它是第一组合类型(也是仅有的一个)。向后扫描\1,指的是任何被\w+所匹配的单词。我们的正则表达式是以能匹配这样的单词,它有一个或多个空格符,后面还跟有一个与此相同的单词。留意的是,尾部的定位类型(\b)必弗成少,它可以防止发生差错。假如我们想匹配"Paris in the the spring",而不是匹配"Java's regex package is the theme of this article"。根据java现在的款式,则上面的正则表达式便是:Pattern pattern =Pattern.compile("\\b(\\w+)\\s+\\1\\b");

最落后一步的改动是让我们的匹配器对大年夜小写敏感。比如,下面的环境:"The the theme of this article is the Java's regex package.",这一点在regex中能异常简单地实现,即经由过程应用在Pattern类中预定义的静态标志CASE_INSENSITIVE :

Pattern pattern =Pattern.compile("\\b(\\w+)\\s+\\1\\b",

Pattern.CASE_INSENSITIVE);

有关正则表达式的话题是异常富厚,而且繁杂的,用Java来实现也异常广泛,则必要对regex包进行的彻底钻研,我们在这里所讲的只是冰山一角。纵然你对正则表达式对照陌生,应用regex包后会很快发明它强大年夜功能和可伸缩性。假如你是个来自Perl或其他说话王国的老练的正则表达式的黑客,应用过regex包后,你将会安心地投入到java的天下,而放弃其他的对象,并把java的regex包当作是手边必备的利器。

您可能还会对下面的文章感兴趣: