wordpress设置多站点,盐城公司做网站,排名函数rank怎么用,石家庄电子商城网站建设深入浅出之正则表达式#xff08;二#xff09; http://dragon.cnblogs.com/archive/2006/05/09/394923.html 前言#xff1a; 本文是前一片文章《深入浅出之正则表达式#xff08;一#xff09;》的续篇#xff0c;在本文中讲述了正则表达式中的组与向后引用…深入浅出之正则表达式二 http://dragon.cnblogs.com/archive/2006/05/09/394923.html 前言 本文是前一片文章《深入浅出之正则表达式一》的续篇在本文中讲述了正则表达式中的组与向后引用先前向后查看条件测试单词边界选择符等表达式及例子并分析了正则引擎在执行匹配时的内部机理。 本文是Jan Goyvaerts为RegexBuddy写的教程的译文版权归原作者所有欢迎转载。但是为了尊重原作者和译者的劳动请注明出处谢谢 9. 单词边界 元字符\b也是一种对位置进行匹配的“锚”。这种匹配是0长度匹配。 有4种位置被认为是“单词边界” 1) 在字符串的第一个字符前的位置(如果字符串的第一个字符是一个“单词字符”) 2) 在字符串的最后一个字符后的位置(如果字符串的最后一个字符是一个“单词字符”) 3) 在一个“单词字符”和“非单词字符”之间其中“非单词字符”紧跟在“单词字符”之后 4) 在一个“非单词字符”和“单词字符”之间其中“单词字符”紧跟在“非单词字符”后面 “单词字符”是可以用“\w”匹配的字符“非单词字符”是可以用“\W”匹配的字符。在大多数的正则表达式实现中“单词字符”通常包括[a-zA-Z0-9_]。 例如\b4\b能够匹配单个的4而不是一个更大数的一部分。这个正则表达式不会匹配“44”中的4。 换种说法几乎可以说\b匹配一个“字母数字序列”的开始和结束的位置。 “单词边界”的取反集为\B他要匹配的位置是两个“单词字符”之间或者两个“非单词字符”之间的位置。 深入正则表达式引擎内部让我们看看把正则表达式\bis\b应用到字符串“This island is beautiful”。引擎先处理符号\b。因为\b是0长度 所以第一个字符T前面的位置会被考察。因为T是一个“单词字符”而它前面的字符是一个空字符(void)所以\b匹配了单词边界。接着i和第一个字符“T”匹配失败。匹配过程继续进行直到第五个空格符和第四个字符“s”之间又匹配了\b。然而空格符和i不匹配。继续向后到了第六个字符“i”和第五个空格字符之间匹配了\b然后is和第六、第七个字符都匹配了。然而第八个字符和第二个“单词边界”不匹配所以匹配又失败了。到了第13个字符i因为和前面一个空格符形成“单词边界”同时is和“is”匹配。引擎接着尝试匹配第二个\b。因为第15个空格符和“s”形成单词边界所以匹配成功。引擎“急着”返回成功匹配的结果。 10. 选择符 正则表达式中“|”表示选择。你可以用选择符匹配多个可能的正则表达式中的一个。 如果你想搜索文字“cat”或“dog”你可以用cat|dog。如果你想有更多的选择你只要扩展列表cat|dog|mouse|fish。 选择符在正则表达式中具有最低的优先级也就是说它告诉引擎要么匹配选择符左边的所有表达式要么匹配右边的所有表达式。你也可以用圆括号来限制选择符的作用范围。如\b(cat|dog)\b这样告诉正则引擎把(cat|dog)当成一个正则表达式单位来处理。 注意正则引擎的“急于表功”性正则引擎是急切的当它找到一个有效的匹配时它会停止搜索。因此在一定条件下选择符两边的表达式的顺序对结果会有影响。假设你想用正则表达式搜索一个编程语言的函数列表GetGetValueSet或SetValue。一个明显的解决方案是Get|GetValue|Set|SetValue。让我们看看当搜索SetValue时的结果。 因为Get和GetValue都失败了而Set匹配成功。因为正则导向的引擎都是“急切”的所以它会返回第一个成功的匹配就是“Set”而不去继续搜索是否有其他更好的匹配。 和我们期望的相反正则表达式并没有匹配整个字符串。有几种可能的解决办法。一是考虑到正则引擎的“急切”性改变选项的顺序例如我们使用GetValue|Get|SetValue|Set这样我们就可以优先搜索最长的匹配。我们也可以把四个选项结合起来成两个选项Get(Value)?|Set(Value)?。因为问号重复符是贪婪的所以SetValue总会在Set之前被匹配。 一个更好的方案是使用单词边界\b(Get|GetValue|Set|SetValue)\b或\b(Get(Value)?|Set(Value)?\b。更进一步既然所有的选择都有相同的结尾我们可以把正则表达式优化为\b(Get|Set)(Value)?\b。 11. 组与向后引用 把正则表达式的一部分放在圆括号内你可以将它们形成组。然后你可以对整个组使用一些正则操作例如重复操作符。 要注意的是只有圆括号“()”才能用于形成组。“[]”用于定义字符集。“{}”用于定义重复操作。 当用“()”定义了一个正则表达式组后正则引擎则会把被匹配的组按照顺序编号存入缓存。当对被匹配的组进行向后引用的时候可以用“\数字”的方式进行引用。\1引用第一个匹配的后向引用组\2引用第二个组以此类推\n引用第n个组。而\0则引用整个被匹配的正则表达式本身。我们看一个例子。 假设你想匹配一个HTML标签的开始标签和结束标签以及标签中间的文本。比如BThis is a test/B我们要匹配B和/B以及中间的文字。我们可以用如下正则表达式“([A-Z][A-Z0-9]*)[^]*.*?/\1” 首先“”将会匹配“B”的第一个字符“”。然后[A-Z]匹配B[A-Z0-9]*将会匹配0到多次字母数字后面紧接着0到多个非“”的字符。最后正则表达式的“”将会匹配“B”的“”。接下来正则引擎将对结束标签之前的字符进行惰性匹配直到遇到一个“/”符号。然后正则表达式中的“\1”表示对前面匹配的组“([A-Z][A-Z0-9]*)”进行引用在本例中被引用的是标签名“B”。所以需要被匹配的结尾标签为“/B” 你可以对相同的后向引用组进行多次引用([a-c])x\1x\1将匹配“axaxa”、“bxbxb”以及“cxcxc”。如果用数字形式引用的组没有有效的匹配则引用到的内容简单的为空。 一个后向引用不能用于它自身。([abc]\1)是错误的。因此你不能将\0用于一个正则表达式匹配本身它只能用于替换操作中。 后向引用不能用于字符集内部。(a)[\1b]中的\1并不表示后向引用。在字符集内部\1可以被解释为八进制形式的转码。 向后引用会降低引擎的速度因为它需要存储匹配的组。如果你不需要向后引用你可以告诉引擎对某个组不存储。例如Get(?:Value)。其中“(”后面紧跟的“?:”会告诉引擎对于组(Value)不存储匹配的值以供后向引用。 重复操作与后向引用当对组使用重复操作符时缓存里后向引用内容会被不断刷新只保留最后匹配的内容。例如([abc])\1将匹配“cabcab”但是([abc])\1却不会。因为([abc])第一次匹配“c”时“\1”代表“c”然后([abc])会继续匹配“a”和“b”。最后“\1”代表“b”所以它会匹配“cabb”。 应用检查重复单词--当编辑文字时很容易就会输入重复单词例如“the the”。使用\b(\w)\s\1\b可以检测到这些重复单词。要删除第二个单词只要简单的利用替换功能替换掉“\1”就可以了。 组的命名和引用在PHPPython中可以用(?Pnamegroup)来对组进行命名。在本例中词法?Pname就是对组(group)进行了命名。其中name是你对组的起的名字。你可以用(?Pname)进行引用。 .NET的命名组 .NET framework也支持命名组。不幸的是微软的程序员们决定发明他们自己的语法而不是沿用Perl、Python的规则。目前为止还没有任何其他的正则表达式实现支持微软发明的语法。 下面是.NET中的例子 (?firstgroup)(?’second’group) 正如你所看到的.NET提供两种词法来创建命名组一是用尖括号“”或者用单引号“’’”。尖括号在字符串中使用更方便单引号在ASP代码中更有用因为ASP代码中“”被用作HTML标签。 要引用一个命名组使用\kname或\k’name’. 当进行搜索替换时你可以用“${name}”来引用一个命名组。 12. 正则表达式的匹配模式 本教程所讨论的正则表达式引擎都支持三种匹配模式 /i使正则表达式对大小写不敏感 /s开启“单行模式”即点号“.”匹配新行符 /m开启“多行模式”即“^”和“$”匹配新行符的前面和后面的位置。 在正则表达式内部打开或关闭模式如果你在正则表达式内部插入修饰符(?ism)则该修饰符只对其右边的正则表达式起作用。(?-i)是关闭大小写不敏感。你可以很快的进行测试。(?i)te(?-i)st应该匹配TEst但是不能匹配teST或TEST. 13. 原子组与防止回溯 在一些特殊情况下因为回溯会使得引擎的效率极其低下。 让我们看一个例子要匹配这样的字串字串中的每个字段间用逗号做分隔符第12个字段由P开头。 我们容易想到这样的正则表达式^(.*?,){11}P。这个正则表达式在正常情况下工作的很好。但是在极端情况下如果第12个字段不是由P开头则会发生灾难性的回溯。如要搜索的字串为“1,2,3,4,5,6,7,8,9,10,11,12,13”。首先正则表达式一直成功匹配直到第12个字符。这时前面的正则表达式消耗的字串为“1,2,3,4,5,6,7,8,9,10,11,”到了下一个字符P并不匹配“12”。所以引擎进行回溯这时正则表达式消耗的字串为“1,2,3,4,5,6,7,8,9,10,11”。继续下一次匹配过程下一个正则符号为点号.可以匹配下一个逗号“,”。然而并不匹配字符“12”中的“1”。匹配失败继续回溯。大家可以想象这样的回溯组合是个非常大的数量。因此可能会造成引擎崩溃。 用于阻止这样巨大的回溯有几种方案 一种简单的方案是尽可能的使匹配精确。用取反字符集代替点号。例如我们用如下正则表达式^([^,\r\n]*,){11}P这样可以使失败回溯的次数下降到11次。 另一种方案是使用原子组。 原子组的目的是使正则引擎失败的更快一点。因此可以有效的阻止海量回溯。原子组的语法是(?正则表达式)。位于(?)之间的所有正则表达式都会被认为是一个单一的正则符号。一旦匹配失败引擎将会回溯到原子组前面的正则表达式部分。前面的例子用原子组可以表达成^(?(.*?,){11})P。一旦第十二个字段匹配失败引擎回溯到原子组前面的^。 14. 向前查看与向后查看 Perl 5 引入了两个强大的正则语法“向前查看”和“向后查看”。他们也被称作“零长度断言”。他们和锚定一样都是零长度的所谓零长度即指该正则表达式不消耗被匹配的字符串。不同之处在于“前后查看”会实际匹配字符只是他们会抛弃匹配只返回匹配结果匹配或不匹配。这就是为什么他们被称作“断言”。他们并不实际消耗字符串中的字符而只是断言一个匹配是否可能。 几乎本文讨论的所有正则表达式的实现都支持“向前向后查看”。唯一的一个例外是Javascript只支持向前查看。 肯定和否定式的向前查看如我们前面提过的一个例子要查找一个q后面没有紧跟一个u。也就是说要么q后面没有字符要么后面的字符不是u。采用否定式向前查看后的一个解决方案为q(?!u)。否定式向前查看的语法是(?!查看的内容)。 肯定式向前查看和否定式向前查看很类似(?查看的内容)。 如果在“查看的内容”部分有组也会产生一个向后引用。但是向前查看本身并不会产生向后引用也不会被计入向后引用的编号中。这是因为向前查看本身是会被抛弃掉的只保留匹配与否的判断结果。如果你想保留匹配的结果作为向后引用你可以用(?(regex))来产生一个向后引用。 肯定和否定式的先后查看向后查看和向前查看有相同的效果只是方向相反 否定式向后查看的语法是(?!查看内容) 肯定式向后查看的语法是(?查看内容) 我们可以看到和向前查看相比多了一个表示方向的左尖括号。 例(?!a)b将会匹配一个没有“a”作前导字符的“b”。 值得注意的是向前查看从当前字符串位置开始对“查看”正则表达式进行匹配向后查看则从当前字符串位置开始先后回溯一个字符然后再开始对“查看”正则表达式进行匹配。 深入正则表达式引擎内部让我们看一个简单例子。 把正则表达式q(?!u)应用到字符串“Iraq”。正则表达式的第一个符号是q。正如我们知道的引擎在匹配q以前会扫过整个字符串。当第四个字符“q”被匹配后“q”后面是空字符(void)。而下一个正则符号是向前查看。引擎注意到已经进入了一个向前查看正则表达式部分。下一个正则符号是u和空字符不匹配从而导致向前查看里的正则表达式匹配失败。因为是一个否定式的向前查看意味着整个向前查看结果是成功的。于是匹配结果“q”被返回了。 我们在把相同的正则表达式应用到“quit”。q匹配了“q”。下一个正则符号是向前查看部分的u它匹配了字符串中的第二个字符“i”。引擎继续走到下个字符“i”。然而引擎这时注意到向前查看部分已经处理完了并且向前查看已经成功。于是引擎抛弃被匹配的字符串部分这将导致引擎回退到字符“u”。 因为向前查看是否定式的意味着查看部分的成功匹配导致了整个向前查看的失败因此引擎不得不进行回溯。最后因为再没有其他的“q”和q匹配所以整个匹配失败了。 为了确保你能清楚地理解向前查看的实现让我们把q(?u)i应用到“quit”。q首先匹配“q”。然后向前查看成功匹配“u”匹配的部分被抛弃只返回可以匹配的判断结果。引擎从字符“i”回退到“u”。由于向前查看成功了引擎继续处理下一个正则符号i。结果发现i和“u”不匹配。因此匹配失败了。由于后面没有其他的“q”整个正则表达式的匹配失败了。 更进一步理解正则表达式引擎内部机制让我们把(?a)b应用到“thingamabob”。引擎开始处理向后查看部分的正则符号和字符串中的第一个字符。在这个例子中向后查看告诉正则表达式引擎回退一个字符然后查看是否有一个“a”被匹配。因为在“t”前面没有字符所以引擎不能回退。因此向后查看失败了。引擎继续走到下一个字符“h”。再一次引擎暂时回退一个字符并检查是否有个“a”被匹配。结果发现了一个“t”。向后查看又失败了。 向后查看继续失败直到正则表达式到达了字符串中的“m”于是肯定式的向后查看被匹配了。因为它是零长度的字符串的当前位置仍然是“m”。下一个正则符号是b和“m”匹配失败。下一个字符是字符串中的第二个“a”。引擎向后暂时回退一个字符并且发现a不匹配“m”。 在下一个字符是字符串中的第一个“b”。引擎暂时性的向后退一个字符发现向后查看被满足了同时b匹配了“b”。因此整个正则表达式被匹配了。作为结果正则表达式返回字符串中的第一个“b”。 向前向后查看的应用我们来看这样一个例子查找一个具有6位字符的含有“cat”的单词。 首先我们可以不用向前向后查看来解决问题例如 cat\w{3}|\wcat\w{2}|\w{2}cat\w|\w{3}cat 足够简单吧但是当需求变成查找一个具有6-12位字符含有“cat”“dog”或“mouse”的单词时这种方法就变得有些笨拙了。 我们来看看使用向前查看的方案。在这个例子中我们有两个基本需求要满足一是我们需要一个6位的字符二是单词含有“cat”。 满足第一个需求的正则表达式为\b\w{6}\b。满足第二个需求的正则表达式为\b\w*cat\w*\b。 把两者结合起来我们可以得到如下的正则表达式 (?\b\w{6}\b)\b\w*cat\w*\b 具体的匹配过程留给读者。但是要注意的一点是向前查看是不消耗字符的因此当判断单词满足具有6个字符的条件后引擎会从开始判断前的位置继续对后面的正则表达式进行匹配。 最后作些优化可以得到下面的正则表达式 \b(?\w{6}\b)\w{0,3}cat\w* 15. 正则表达式中的条件测试 条件测试的语法为(?ifthen|else)。“if”部分可以是向前向后查看表达式。如果用向前查看则语法变为(?(?regex)then|else)其中else部分是可选的。 如果if部分为true则正则引擎会试图匹配then部分否则引擎会试图匹配else部分。 需要记住的是向前先后查看并不实际消耗任何字符因此后面的then与else部分的匹配时从if测试前的部分开始进行尝试。 16. 为正则表达式添加注释 在正则表达式中添加注释的语法是(?#comment) 例为用于匹配有效日期的正则表达式添加注释 (?#year)(19|20)\d\d[- /.](?#month)(0[1-9]|1[012])[- /.](?#day)(0[1-9]|[12][0-9]|3[01])