java正则表达式tab_JAVA正则表达式(超详细)
在Sun的Java JDK 1.40版本中,Java⾃带了⽀持正则表达式的包,本⽂就抛砖引⽟地介绍了如何使⽤包。
可粗略估计⼀下,除了偶尔⽤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这样的⼀个很⼤改进(⾃带了包)为什么不多公开⼀点呢?!
最近,Java双脚都跳进了正则表达式的世界。包在⽀持正则表达也有它的过⼈之处,另外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 *;
下⾯的⼀段代码实现的功能是,从⼀个⽂本⽂件逐⾏读⼊,并逐⾏搜索电话号码数字,⼀旦找到所匹配的,然后输出在控制台。
BufferedReader in;
Pattern pattern = pile("//(//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.up());
}
}
in.clo();
对那些熟悉⽤Python或Javascript来实现正则表达式的⼈来说,这段代码很平常。在Python和Javascript这些语⾔中,或者其他的语⾔,这些正则表达式⼀旦明确地编译过后,你想⽤到哪⾥都可以。与Perl的单步匹配相⽐,看起来多多做了些⼯作,但这并不很费事。
find()⽅法,就像你所想象的,⽤来搜索与正则表达式相匹配的任何⽬标字符串,group()⽅法,⽤来返回包含了所匹配⽂本的字符串。应注意的是,上⾯的代码,仅⽤在每⾏只能含有⼀个匹配的电话号码
数字字符串时。可以肯定的说,java的正则表达式包能⽤在⼀⾏含有多个匹配⽬标时的搜索。本⽂的原意在于举⼀些简单的例⼦来激起读者进⼀步去学习java⾃带的正则表达式包,所以对此就没有进⾏深⼊的探讨。
这相当漂亮吧! 但是很遗憾的是,这仅是个电话号码匹配器。很明显,还有两点可以改进。如果在电话号码的开头,即区位号和本地号码之间可能会有空格。我们也可匹配这些情况,则通过在正则表达式中加⼊/s?来实现,其中?元字符表⽰在模式可能有0或1个空格符。菊花泡水喝的功效与作用
第⼆点是,在本地号码位的前三位和后四位数字间有可能是空格符,⽽不是连字号,更有胜者,或根本就没有分隔符,就是7位数字连在⼀起。对这⼏种情况,我们可以⽤(-|)?来解决。这个结构的正则表达式就是转换器,它能匹配上⾯所说的⼏种情况。在()能含有管道符|时,它能匹配是否含有空格符或连字符,⽽尾部的?元字符表⽰是否根本没有分隔符的情况。
最后,区位号也可能没有包含在圆括号内,对此可以简单地在圆括号后附上?元字符,但这不是⼀个很好的解决⽅法。因为它也包含了不配对的圆括号,⽐如"(555" 或 "555)"。相反,我们可以通过另⼀种转换器来强迫让电话号码是否带有有圆括号:(/(/d{3}/)|/d{3})。如果我们把上⾯代码中的正则表达式⽤这些改进后的来替换的话,上⾯的代码就成了⼀个⾮常有⽤的电话号码数字匹配器:
Pattern pattern =
伴侣誓言
可以确定的是,你可以⾃⼰试着进⼀步改进上⾯的代码。
现在看看第⼆个例⼦,它是从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 =pile("//b(//w+)//s+//1//b");
最后进⼀步的修改是让我们的匹配器对⼤⼩写敏感。⽐如,下⾯的情况:"The the theme of this article is the Java's regex package.",这⼀点在regex中能⾮常简单地实现,即通过使⽤在Pattern类中预定义的静态标志CASE_INSENSITIVE :
Pattern pattern =pile("//b(//w+)//s+//1//b",
Pattern.CASE_INSENSITIVE);
有关正则表达式的话题是⾮常丰富,⽽且复杂的,⽤Java来实现也⾮常⼴泛,则需要对regex包进⾏的彻底研究,我们在这⾥所讲的只是冰⼭⼀⾓。即使你对正则表达式⽐较陌⽣,使⽤regex包后会很快发现它强⼤功能和可伸缩性。如果你是个来⾃Perl或其他语⾔王国的⽼练的正则表达式的***,使⽤过regex包后,你将会安⼼地投⼊到java的世界,⽽放弃其他的⼯具,并把java的regex包看成是⼿边必备的利器。
CharSequence
JDK 1.4定义了⼀个新的接⼝,叫CharSequence。它提供了String和StringBuffer这两个类的字符序列的抽象:interface CharSequence {
charAt(int i);
韩式除皱length();
subSequence(int start, int end);
toString();
}
为了实现这个新的CharSequence接⼝,String,StringBuffer以及CharBuffer都作了修改。很多正则表达式的操作都要拿CharSequence作参数。
Pattern和Matcher
先给⼀个例⼦。下⾯这段程序可以测试正则表达式是否匹配字符串。第⼀个参数是要匹配的字符串,
后⾯是正则表达式。正则表达式可以有多个。在Unix/Linux环境下,命令⾏下的正则表达式还必须⽤引号。//: c12:TestRegularExpression.java
// Allows you to easly try out regular expressions.
// {Args: abcabcabcdefabc "abc+" "(abc)+" "(abc){2,}" }
import *;
publicclass TestRegularExpression {
publicstaticvoid main(String[] args) {
if(args.length < 2) {
System.out.println("Usage:/n" +
"java TestRegularExpression " +
饮用天然水
"characterSequence regularExpression+");
}
System.out.println("Input: /"" + args[0] + "/"");
for(int i = 1; i < args.length; i++) {
System.out.println(
"Regular expression: /"" + args[i] + "/"");
Pattern p = pile(args[i]);
Matcher m = p.matcher(args[0]);
while(m.find()) {
System.out.println("Match /"" + m.group() +
"/" at positions " +
m.start() + "-" + (m.end() - 1));不甘示弱的近义词
}
}
}
} ///:~
Java的正则表达式是由的Pattern和Matcher类实现的。Pattern对象表⽰经编译的正则表达式。静态的compile( )⽅法负责将表⽰正则表达式的字符串编译成Pattern对象。正如上述例程所⽰的,只要给Pattern的matcher( )⽅法送⼀个字符串就能获取⼀个Matcher对象。此外,Pattern还有⼀个能快速判断能否在input⾥⾯找到regex的
staticboolean matches(?regex, ?input)
以及能返回String数组的split( )⽅法,它能⽤regex把字符串分割开来。
只要给Pattern.matcher( )⽅法传⼀个字符串就能获得Matcher对象了。接下来就能⽤Matcher的⽅法来查询匹配的结果了。
boolean matches()
boolean lookingAt()
boolean find()
boolean find(int start)
matches( )的前提是Pattern匹配整个字符串,⽽lookingAt( )的意思是Pattern匹配字符串的开头。
find( )
Matcher.find( )的功能是发现CharSequence⾥的,与pattern相匹配的多个字符序列。例如://: c12:FindDemo.java
import *;
import com.bruceeckel.simpletest.*;
import java.util.*;
publicclass FindDemo {
privatestatic Test monitor = new Test();
publicstaticvoid main(String[] args) {
Matcher m = pile("//w+")
.matcher("Evening is full of the linnet's wings");
while(m.find())
System.out.up());
int i = 0;
while(m.find(i)) {
System.out.up() + " ");
i++;
}
西瓜英语>禹王庙
"Evening",
"is",
"full",
"of",
"the",
"linnet",
"s",
"wings",
"Evening vening ening ning ing ng g is is s full " +
"full ull ll l of of f the the he e linnet linnet " +
"innet nnet net et t s s wings wings ings ngs gs s "
});
}
} ///:~
"//w+"的意思是"⼀个或多个单词字符",因此它会将字符串直接分解成单词。find( )像⼀个迭代器,从头到尾扫描⼀遍字符串。第⼆个find( )是带int参数的,正如你所看到的,它会告诉⽅法从哪⾥开始找——即从参数位置开始查找。
Groups
Group是指⾥⽤括号括起来的,能被后⾯的表达式调⽤的正则表达式。Group 0 表⽰整个表达式,group 1表⽰第⼀个被括起来的group,以此类推。所以;
A(B(C))D
⾥⾯有三个group:group 0是ABCD, group 1是BC,group 2是C。
你可以⽤下述Matcher⽅法来使⽤group:
public int groupCount( )返回matcher对象中的group的数⽬。不包括group0。
public String group( )返回上次匹配操作(⽐⽅说find( ))的group 0(整个匹配)
public String group(int i)返回上次匹配操作的某个group。如果匹配成功,但是没能找到group,则返回null。
public int start(int group)返回上次匹配所找到的,group的开始位置。
public int end(int group)返回上次匹配所找到的,group的结束位置,最后⼀个字符的下标加⼀。//: c12:Groups.java
import *;
import com.bruceeckel.simpletest.*;
publicclass Groups {
privatestatic Test monitor = new Test();
staticpublicfinal String poem =