Java & regex(2-3)
发表于:2007-07-04来源:作者:点击数:
标签:
Java regex(2-3) §5 连接 核心3点是rs表达式。这个连接运算,再说就似乎太罗索了。把单字符顺着写就形成了一条字符线,一个字符接着一个字符。在Java中,有字符串String、字符序列(CharSequence),虽然都是一根绳子,但它们不是一个东西——String是类、
Java & regex(2-3)
§5 连接
核心3点是rs表达式。这个连接运算,再说就似乎太罗索了。把单字符顺着写就形成了一条字符线,一个字符接着一个字符。在Java中,有字符串String、字符序列(CharSequence),虽然都是一根绳子,但它们不是一个东西——String是类、CharSequence是接口。ok,我们不讨论Java的API。
这里要注意的是,
在使用连接运算时记住:
① 连接运算仅仅比元字符|的优先级高。
boy是一个regex,其语义是b后面紧跟一个o再紧跟一个y。包括了元字符的时候,如b[ao]y其语义是b后面紧跟一个a或o再紧跟一个y。
② 所有的并集(除了ba|oy形式)仅仅匹配一个单字符,例如:
a(sd|f)g匹配Iasdgbbasgbbafgbb,而a[sd|f]g匹配asdgooasgoooafgooa|gooadg【并集还有很多东西要学习的。】
③ 关于.的问题。
有一个典型的例子——日期的匹配说明,我们应该谨慎使用它。日期格式一般是yyyy-mm-dd。当然也有yyyy.mm.dd等形式,如果使用\d\d\d\d.\d\d\.\d\d,虽然可以匹配用户喜欢的日期分割符,但它也匹配2005a02b02,9876543210这不是我们希望的。也许使用[-/ .]顶替.比较好。它允许a dash, space, dot and forward slash作为日期分割符。【当然它也不完善,因为它匹配3005/13/50和0000/00/00。而它不匹配我写的今天的日期格式05/2/2。事实上,regex的构造依赖于我们的目标——如果我们强制用户使用yyyy/mm/dd格式,我们可以简单的用:
(19|20)\d\d/(0[1-9]|1[012])/(0[1-9]|[12][0-9]|3[01]) 。】
④ 连接的其他形式:
正如我们说知道的,a{3}是aaa的简写。a{1,3}是a|aa|aaa的简写。我们常常把它们与闭包运算放在一起讨论。
§6 闭包
核心4点是r*表达式。我们首先回顾闭包运算的各种写法,再深入理解正则表达式引擎的运行规则。
L (r*) = {ε,r,rr,……}是无穷集合,它匹配r串的任意有穷连结。
在Java中:
r?是{ε,r }的正则表达式;
r+是L (r*)-{ε}的正则表达式;
r{3,} 是L (r*)-{ε,r,rr}的正则表达式;
在涉及闭包运算时,我们会遇到几个重要概念——Greediness(贪心)、lookahead和lookbehind(瞻前顾后——前瞻、后顾)等。我们先看几个例子:
regex
String
替换⊙
a+
saaaasgaaafga
s⊙sg⊙fg⊙
ab?\w
abc aaabc gabbbf gbbaaag
a?
far【有四个匹配项】
⊙f⊙⊙r⊙
[ab]+
back about bar bbb aaa bac
[ab]+[rc]
back about bar bbb aaa bac
[ab]*
back about bar bbb aaa bac
⊙⊙c⊙k⊙ ⊙⊙o⊙u⊙t⊙ ⊙⊙r⊙ ⊙⊙ ⊙⊙ ⊙⊙c⊙
[0-9]+
123456654321999ok
⊙ok
[3-6]+
111333555888
([3-9])\\1+
12355555551999ok
[ab]{3,}
abc aaabc gabbbf gbbaaag
解释:单独使用a?和a*很麻烦。
([3-6])\\1+【regex练习器与Java源程序不同!】
§7 regex引擎的机制
regex引擎是处理正则表达式的软件,尝试以模板去匹配给定的字符串。一般而言,我们不直接调用该引擎,而是通过一些API去使用它们。对于不同的语言和
开发环境,它们不会完全一致,其中Perl 5的regex是基础,它也是使用得最广泛的。Java语言的regex与Perl 5的regex flavor有一些不同。但机制是一致的。
1、匹配的两种道路:
有两种regex引擎:文本引导(text-directed)引擎和regex引导的引擎。它们
2、regex引擎是急性子,它总是返回最左边的匹配项
必须记住的非常重要的一点:regex引导的引擎总是返回最左边的匹配项。我在前面很小心的说某个regex可以匹配那些咚咚,是因为我们使用了一些Java语言的方法,regex引擎总是从字符串头开始匹配(start from beginning),一旦找到了一个匹配项,它就会急急忙忙的报告说:“我找到了匹配项”。除非你要求它again。
对于文本good and god,regex为go{1,2}d,则返回最左边的good。我们详细说明其过程。
regex引擎从字符串头开始匹配,①g显然匹配No.为0的g。②o{1,2}怎么办,引擎还有点聪明,它首先研究一下o{1,2}的语义,表示一个或两个o,于是它首先匹配了No.为1的o。它发现No.为2的o也可以匹配,于是完成了o{1,2}的任务。③现在,引擎开始匹配d,No.为3的d有效。ok,引擎完成了其工作。它不会再继续匹配了。
如果我们让它继续匹配呢?它认为已经走过的路不需要再重复了,于是新的开始点是No.为4的空格,即 and god,①g不匹配空格,②g不匹配a、n、d、空格。③g显然匹配No.为5(新的No.)的g。引擎还有点聪明,它又研究一下o{1,2}的语义,它首先匹配了No.为6的o。当遇到d它知道o{1,2}的任务结束了,它发现d匹配了No.为7的d。ok,引擎完成了其工作。
3、为什么regex引擎是急性子?
现在我们要求匹配一下setName方法,假设regex为get|set|setName,它的返回的匹配项是什么?yes,仅仅是set。我们看看引擎是如何工作的:
①引擎首先研究了regex,把各种可能的组合了解清楚了。于是它开始匹配,首先它拿g匹配s,失败了,它知道get整个失败了。但是它知道还有其他可能的组合。②于是它开始s匹配s,成功,于是set匹配set成功。③我找到了匹配项了。它用set匹配了字符串之后,不会继续匹配“更好”的选项。
显然,我们没有找到我们希望的setName方法,how to fix it?最简单的方法是regex为get|setName|set。按照上面的过程,我们能够匹配setName。这是我们希望的。
4、为什么regex引擎有点聪明?
它有点聪明,不仅仅是说它开始匹配前,会研究一下regex。更表现在它有一点记性。regex为get|setName|set要求匹配一下setGame。说说其匹配过程。前面的废话不说了,当N匹配G时,失败。于是regex引擎用第3个可能性s匹配No.为0的s,regex引擎居然还知道从No.为0的地方开始。现在思考,匹配setGamesetName的结果是什么?set还是setName。
5、Greediness(贪心)匹配:
get|setName|set可以修改一下,成get|set(Name)?。它将如何匹配setName。两个regex完全一样。ba?运算先匹配ba,再匹配a。这就是我们所说的Greedy quantify的用法。也就是贪婪匹配或最大匹配。
注意:虽然JDK文档中,在Greedy quantify下面列出了X{n}、X{n,m},它们不过是连接而已,与最大匹配没有关系。
X?、X*、X+、X{n,}都是最大匹配。
最大匹配与. 同时使用时,常常带来麻烦。以”.*”匹配I love “Java” and “C++”,其返回的是“Java” and “C++” 。也许我们希望它匹配“Java” 。我们使用否定式取代. 的情况非常普遍,如”[^”\r\n]*”,这样我们就可以匹配出我们希望的“Java” 。
对于Java的关键字,我们如何效验其正确性(不考虑关键字和unicode的情况)?可以这样编写表达式[a-zA-Z_$][0-9a-zA-Z_$]*。也可以这样编写表达式[a-zA-Z_$][\w$]*。
6、backtrack、(由原路)返回
我们看看<.+>如何匹配HTML的标签a<tr>Java </tr>cb。按照我们前面讨论的方式:①<匹配No.-1的<。②.+ 进行匹配tr>Java </tr>cb,在进行最大匹配时,它把两个>匹配了,它一直匹配所有的字符,直到文本的最后b后面不能匹配了。③这时>开始匹配,前面的最大匹配已经匹配到文本的最后b后面了,当然>匹配失败。④regex引擎心里明白了,那个+匹配得太多了。但regex引擎这时并不报错,它由原路返回一个字符,它认为.+匹配了tr>Java </tr>c于是用>匹配b,失败again;它再由原路返回一个字符,认为.+匹配tr>Java </tr>,用>匹配c,失败……最后终于用>匹配>,大功告成。regex引擎是急性子,它不想再退了。
现在以<.+>aa匹配a<tr>aava </tr>abb,你可以想一想,我再说,嘴巴都要破了。不说了。如果以<.+>aa.+a匹配a<tr>aava </tr>abb,罪过啊。
string
regex
a<tr>aava </tr>abb
<.+>
a<tr>aava </tr>abb
<.+>aa
a<tr>aava </tr>abb
<.+>aa.+a
a<tr>aava </tr>abb
<tr>|</tr>
7、最小匹配
如果想匹配出各种HTML的标签,<tr>|</tr>显然不行。于是,我们需要一些非贪婪匹配。
X?、X*、X+、X{n,}都是最大匹配。好,加个?就成了Laziness匹配。例如X??、X*?、X+?、X{n,}?都是最小匹配,其实X{n,m}?和X{n }?有些多余。
string
regex
a<tr>aava </tr>abb
<.+?>
a<tr>aava </tr>abb
<.+>a?a
a<tr>aava </tr>abb
<.*?>
a<tr>aava </tr>abb
a{1,2}?
a<tr>aava </tr>abb
a{2}?
最小匹配意味者,.+? 匹配一个字符后,马上试一试>的匹配可能,失败了,则.+? 再匹配一个字符,再马上试一试>的匹配可能。JDK文档中Greedy 和 Reluctant,它是以eat一口来隐喻的,所以翻译成贪吃和(勉强的)厌食最贴切了。不过我喜欢最大匹配、最小匹配的说法。
8、完全匹配
与最大匹配不同,还有一种匹配形式:X?+、X*+、X++、X{n,}+等,成为完全匹配。它和最大匹配一样,一直匹配所有的字符,直到文本的最后,但它不由原路返回。也就是说,一口匹配,搞不定就算了,到也干脆,偶喜欢。
string
regex
a<tr>aava </tr>abb【No match found.】
a.++b
【空行也能匹配,ε】
.*+
aabb【匹配aabb和ε】
.*+
a<tr>aava </tr>abb
a{1,2}?
a<tr>aava </tr>abb
a{2}?
Use a possessive quantifier for situations where you want to seize all of something without ever backing off; it will outperform the equivalent greedy quantifier in cases where the match is not immediately found.
原文转自:http://www.ltesting.net