4.5.1 Problems of Greediness
既然,我们知道「.*」可以匹配到一行文本的末尾。
那我们用「”.*”」匹配下面的文本:
Thename “McDonald’s” is said “makudonarudo” in Japanese.
程序如下:
#! /usr/bin/perl -w
$str1 = "The name \"McDonald's\" is said \"makudonarudo\" in Japanese.";
$str1 =~ /(".*")/;
print $1;
其执行结果为:
$perl mre45_1
"McDonald's"is said "makudonarudo"
这显然不是我们期望的结果。那么,我们如何能够只取得‘”mcDonald’s”’呢?关键的问题在于要认识到,我们希望匹配的不是双引号之间的“任何文本”,而是“除双引号以外的任何文本”。使用「[^”]*」替代「.*」,就不会出现上面的问题。
程序如下:
#! /usr/bin/perl -w
$str1 = "The name \"McDonald's\" is said \"makudonarudo\" in Japanese.";
# $str1 =~ /(".*")/;
$str1 =~ /("[^"]*")/;
print $1;
第一个双引号匹配之后,「[^”]*」会匹配尽可能多的字符。因为「[^”]」无法匹配之后的双引号,所以,匹配的字符串就是‘McDonald’s’。此时,控制权转移到正则表达式末尾的「”」。而他刚好能够匹配,所以获得全局匹配。
4.5.2 Multi-Character “Quotes”
我们再来看一个例子使用「.*」匹配多字符“引文”
<B>Billions</B>and <B>Zillions</B> of suns…
正则表达式「<B>.*</B>」中匹配优先的「.*」会一直匹配该行结尾的字符,回溯只会进行到「</B>」能够匹配为止,也就是最后一个’</B>’,而不是与匹配开头的「<B>」对应得「</B>」。
程序如下:
#! /usr/bin/perl -w
$str2 = "<B>Billions</B> and <B>Zillions</B> of suns";
$str2 =~ m{(<B>.*</B>)};
print $1;
4.5.3 Using Lazy Quantifiers
上面的问题之所以会出现,原因在于标准量词是匹配优先的。某些NFA支持忽略优先的量词,*?就是与*对应得忽略优先量词。
我们用「<B>.*?</B>」来匹配上例中的字符串:
<B>Billions</B>and <B>Zillions</B> of suns…
开始的「<B>」匹配之后,「*?」首先决定不需要匹配任何字符,因为他是忽略优先的。于是控制权交给后面的「<」符号:
‘…<B>▲Billions…’
|
「<B>.*?▲</B>」
|
此时「<」无法匹配,所以控制权交还给「.*?」,因为还有未尝试过的匹配可能(事实上能够进行多次匹配尝试)。它的匹配尝试是步步为营的(begrudgingly),先用点号来匹配…<B>Billions…中带下划线的B。此时,*?又必须选择,是继续尝试匹配,还是忽略?因为它是忽略优先的,会首先选择忽略。接下来的「<」仍然无法匹配,所以「.*」必须继续尝试未匹配的分支。在这个过程重复8次后,「.*」最终匹配了‘Billions’,此时,接下来的「<」(以及整个「</B>」)都能匹配:
…<B>Billions</B> and<B>Zillions</B> of suns…
程序如下:
#! /usr/bin/perl -w
$str2 = "<B>Billions</B> and <B>Zillions</B> of suns";
$str2 =~ m{(<B>.*?</B>)};
print $1;
如果用上述程序匹配下列字符串会怎样呢?
…<B>Billions and <B>Zillions</B> of suns…
这种情况下的结果不是用户期望的。不过,「.*?」必然会匹配‘Zillions’左边的<B>,一直到</B>。
这个例子很好地说明了,为什么通常情况下,忽略优先量词并不是排除类的完美替身。
如果我们使用否定环视,就能得到想要的结果。
请看下面表达式:
<B> # Match the opening<B>
( # Now, only asmany of the following as needed …
(?!<B> ) # If not <B> …
. # … any character is okay
)*? #
</B> # … until the closingdelimiter can match
程序如下:
i��n-f�(F�imes New Roman";mso-hansi-font-family:"Times New Roman"'>」。
程序如下:
#! /usr/bin/perl -w
$str2 = "<B>Billions and <B>Zillions</B> of suns";
$str2 =~ m{
(
<B>
(
(?! <B> )
.
)*?
</B>
)
}x;
print $1;
执行结果:
$perl mre45_24.pl
<B>Zillions</B>
使用了环视功能之后,我们可以重新使用普通的匹配优先量词:
<B> # Match the opening<B>
( # Now, as manyof the following as possible …
(?!< /? B> ) # If not <B>, and not</B> …
. # … any character isokay
)* # (now greedy)
</B> # <ANNO> … until the closingdelimiter can match.
程序如下:
#! /usr/bin/perl -w
$str2 = "<B>Billions and <B>Zillions</B> of suns";
$str2 =~ m{
(
<B>
(
(?! < /? B> )
.
)*
</B>
)
}x;
print $1;
执行结果:
$perl mre45_25.pl
<B>Zillions</B>
4.5.4 Greediness and LazinessAlways Favor a Match
第2章中显示价格的例子,我们会在本章的多个地方仔细检查这个例子,因为浮点数的显示问题,“1.625”或者“3.00”有时候会变成“1.62500000002828”和“3.0000000002882”。为解决这个问题,使用如下正则:
a.$price =~ s/(\.\d\d[1-9]?)\d*/$1/;
「\.\d\d」匹配最开始两位数字,而「[1-9]?」用来匹配可能出现的不等于0的第三位数字。
到现在看起来一切正常,但是,如果$price的数据本身格式规范,会出现什么问题呢?就是用‘.625’替换‘.625’—相当于白费功夫。
所以,我们用如下表达式:
b.$price =~ s/(\.\d\d[1-9]?)\d+/$1/
但是a处理格式规范的数据会降低效率,使用b处理规范数据还匹配不上。
例如:
#! /usr/bin/perl -w
$price = 9.436;
$price =~ s/(\.\d\d[1-9]?)\d+/$1/;
# $price =~ s/(\.\d\d[1-9]?)\d*/$1/;
print $1;
执行结果:
$perl mre45_3.pl
.43
结果不是我们想要的‘.436’!
4.5.5 The Essence of Greediness, Laziness, and Backtracking
之前的章节告诉我们,正则中的某个元素,无论是匹配优先,还是忽略优先,都是为全局匹配服务的。
他们(匹配优先或忽略优先),在遇到“本地匹配失败”时,引擎都会回归到备用状态,然后尝试尚未尝试的路径。
如果存在不止一个可能的匹配结果,那么匹配优先匹配最长的结果,而使用忽略优先的匹配最短的结果。
如下程序所示:
#! /usr/bin/perl -w
$str1 = "The name \"McDonald's\" is said \"makudonarudo\" in Japanese.";
$str1 =~ /(".*")/;
print "$1\n";
$str1 =~ /(".*?")/;
print $1;
执行结果:
$perl mre45_12.pl
"McDonald's"is said "makudonarudo"
"McDonald's"
分享到:
相关推荐
linux系统shell正则表达式-练习工具和教材 正则表达式-练习工具和教材 正则表达式-练习工具和教材
这是编译原理的一个实验, 是把一个正则表达式转化为不确定有穷自动机NFA的算法程序,朋兴趣的朋友可以下载来看看哦. 一个正则表达式就是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式...
包括ORACLE+PLSQL正则表达式手册、详细的Javascript正则表达式参考手册、C#字符串和正则表达式参考手册
正则表达式规则及常用正则表达式列举,应该很全了
basics - Bin-block 全局解释器锁练习源码 - Bin-Coroutine 协程练习源码 + Coroutine 协程练习源码 + greenlet 模块 - Bin-Thread 线程练习源码 - Bin-Multiprocessing 多进程练习 - Bin-Re 正则表达式练习 ...
javascript的正则表达式的经典案例详解,从入门到熟练~
学习完正则表达式后,有必要通过一定的习题来检验自己的学习情况
正 则 表 达 式 学 习 笔 记
编译原理课的大作业 包含三个小实验 在一个cpp文件里 正则表达式转换为nfa nfa转换为dfa dfa最小化 个人原创代码
6.11正则化复杂度调节和剪枝 本章小结 文献和历史评述 习题 上机练习 参考文献 ------------------- 第七章随机方法 7.1引言 7.2随机搜索 7.3学习 7.4网络和图示模型 7.5进化方法 7.6规则 本章小结 ...
6.11正则化复杂度调节和剪枝 本章小结 文献和历史评述 习题 上机练习 参考文献 ------------------- 第七章随机方法 7.1引言 7.2随机搜索 7.3学习 7.4网络和图示模型 7.5进化方法 7.6规则 本章小结 ...
基础篇12-python基本数据结构-字典 基础篇13-python基本数据结构习题解答 基础篇14-答疑课-python里面这些难缠的符号们 基础篇15-答疑课-再议数据结构与数据类型 基础篇16-python语句1.1 基础篇17-python语句1.2 ...
这个资源有问题,已损坏,需要的朋友请下载“编译原理第三部分(重发)”,给大家带来麻烦对不起了~~~ 西北工业大学出版,第三版,蓝色封面,课后习题答案,一共三个部分,可单独解压,
这里给大家详细讲解一下一个匹配IP地址的正则表达式, 有关正则方面的知识,会在详细的讲解中提到。 在讲解之前,我先给大家介绍一下,ip地址的生成规则。 IP地址,是由32位数字二进制转为四个十进制的字符串...
此课程较全面的讲解了正则表达式,之后还有习题供做训练。适合初学者。
第 5 讲 不动点原理及应用 第 6 讲 紧集与连续映射 第 7 讲 紧性与有限维空间 第 8 讲 积空间与商空间 习 题 一 第二章 有界线性算子与有界线性泛函 第 9 讲 空间 B(X, Y) 与 X* 第 10 讲 共鸣...
编译原理正则式转NFA转DFA以及最小化
JAVA开发实战经典课后习题讲解第11章:正则习题讲解
包含一些实例,以及课件习题,同样也包含一些可供开发时参考的代码,又陷入深。
第四章详细答案解析 文法与语法分析,文法的二义性,等价文法,算法,正则nfa,dfa到正则文法的转换,判断文法是LL(1)文法,文法分析表,文法分析过程