入门4

核心3rs表达式。这个连接运算,再说就似乎太罗索了。把单字符顺着写就形成了一条字符线,一个字符接着一个字符。在Java中,有字符串String字符序列CharSequence),虽然都是一根绳子,但它们不是一个东西——String是类、CharSequence是接口。ok,我们不讨论JavaAPI

这里要注意的是,

在使用连接运算时记住:

    连接运算仅仅比元字符|的优先级高。

        boy是一个regex,其语义是b后面紧跟一个o再紧跟一个y。包括了元字符的时候,如b[ao]y其语义是b后面紧跟一个ao再紧跟一个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,虽然可以匹配用户喜欢的日期分割符,但它也匹配2005a02b029876543210这不是我们希望的。也许使用[-/ .]顶替.比较好。它允许a dash, space, dot and forward slash作为日期分割符。【当然它也不完善,因为它匹配3005/13/500000/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{13}a|aa|aaa的简写。我们常常把它们与闭包运算放在一起讨论。

§6 闭包

核心4r*表达式。我们首先回顾闭包运算的各种写法,再深入理解正则表达式引擎的运行规则。

L (r*) = {ε,r,rr,……}是无穷集合,它匹配r串的任意有穷连结。

Java中:

r{ε,r }正则表达式;

r+L (r*)-{ε}正则表达式;

r{3,} L (r*)-{ε,r,rr}正则表达式;

在涉及闭包运算时,我们会遇到几个重要概念——Greediness(贪心)、lookaheadlookbehind(瞻前顾后——前瞻、后顾)等。我们先看几个例子:

regex

String

替换⊙

a+

saaaasgaaafga

ssgfg

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

⊙⊙ck ⊙⊙out ⊙⊙r ⊙⊙ ⊙⊙ ⊙⊙c

[0-9]+

123456654321999ok

ok

[3-6]+

111333555888

 

([3-9])//1+

12355555551999ok

 

[ab]{3,}

abc aaabc gabbbf gbbaaag

 

 

 

 

解释:单独使用aa*很麻烦。

([3-6])//1+regex练习器与Java源程序不同!】

§7 regex引擎的机制

regex引擎是处理正则表达式的软件,尝试以模板去匹配给定的字符串。一般而言,我们不直接调用该引擎,而是通过一些API去使用它们。对于不同的语言和开发环境,它们不会完全一致,其中Perl 5regex是基础,它也是使用得最广泛的。Java语言的regexPerl 5regex flavor有一些不同。但机制是一致的。

1、匹配的两种道路:

       有两种regex引擎:文本引导(text-directed)引擎和regex引导的引擎。它们

2、regex引擎是急性子,它总是返回最左边的匹配项

必须记住的非常重要的一点:regex引导的引擎总是返回最左边的匹配项。我在前面很小心的说某个regex可以匹配那些咚咚,是因为我们使用了一些Java语言的方法,regex引擎总是从字符串头开始匹配(start from beginning),一旦找到了一个匹配项,它就会急急忙忙的报告说:“我找到了匹配项”。除非你要求它again

对于文本good and godregexgo{1,2}d则返回最左边的good。我们详细说明其过程。

regex引擎从字符串头开始匹配,①g显然匹配No.0g。②o{1,2}怎么办,引擎还有点聪明,它首先研究一下o{1,2}的语义,表示一个或两个o,于是它首先匹配了No.1o。它发现No.2o也可以匹配,于是完成了o{1,2}的任务。③现在,引擎开始匹配dNo.3d有效。ok引擎完成了其工作。它不会再继续匹配了。

如果我们让它继续匹配呢?它认为已经走过的路不需要再重复了,于是新的开始点是No.4的空格,即 and god,①g不匹配空格,②g不匹配and、空格。③g显然匹配No.5(新的No.)的g。引擎还有点聪明,它又研究一下o{1,2}的语义,它首先匹配了No.6o。当遇到d它知道o{1,2}的任务结束了,它发现d匹配了No.7dok引擎完成了其工作。

 

3、为什么regex引擎是急性子?

现在我们要求匹配一下setName方法,假设regexget|set|setName,它的返回的匹配项是什么?yes,仅仅是set我们看看引擎是如何工作的:

①引擎首先研究了regex,把各种可能的组合了解清楚了。于是它开始匹配,首先它拿g匹配s,失败了,它知道get整个失败了。但是它知道还有其他可能的组合。②于是它开始s匹配s,成功,于是set匹配set成功。③我找到了匹配项了。它用set匹配了字符串之后,不会继续匹配“更好”的选项。

显然,我们没有找到我们希望的setName方法,how to fix it?最简单的方法是regexget|setName|set。按照上面的过程,我们能够匹配setName。这是我们希望的。

 

4、为什么regex引擎有点聪明?

它有点聪明,不仅仅是说它开始匹配前,会研究一下regex。更表现在它有一点记性。regexget|setName|set要求匹配一下setGame。说说其匹配过程。前面的废话不说了,当N匹配G时,失败。于是regex引擎用第3个可能性s匹配No.0sregex引擎居然还知道从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>abbNo 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.

发布了30 篇原创文章 · 获赞 0 · 访问量 2万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章