本帖最后由 shqf 于 2014-8-12 08:55 编辑
接一楼:
一、正则表达式中的匹配、捕获与输出。
许多正则教程中往往没有细分匹配、捕获与输出概念,对三者的关系更是很少涉及。这导致一些初学者混同了匹配与输出,或割裂了匹配与捕获,写出的正则表达式往往达不到效果,但又不知错在哪里,难以快速入门。我在学习正则的过程中,越来越感到如能正确理解匹配、捕获与输出的概念及关系,就好比拿到了进入正则殿堂的一把金钥匙,可以快速地入门。
那么究竟什么是正则表达式中的匹配、捕获与输出呢?
我们常说的匹配,即某个字符串匹配某个正则表达式,通常是指这个字符串里有一部分(或几部分分别)能满足表达式给出的条件。
关于捕获,许多教程未单独讲到。其往往包含在捕获组概念中。捕获组是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用。其往往对捕获内容的编号或命名及如何引用的讲解得比较多。但有一点请注意,子表达式匹配的内容未命名的编号从1开始,编号为0的捕获组,指的是正则表达式整体匹配的内容,这一规则在支持捕获组的语言中,基本上都是适用的。(参见文章《正则基础之——捕获组(capture group)》,原作者不明,但百度一下就会搜到很多)。为了把一般教程称之的捕获组与0号捕获组加以区别,我把一般教程所谓的捕获组称之为子表达式捕获组,下分仍旧为普通捕获组和命名捕获组。
输出是指正则表达式想要得到的结果, AU3中即为StringRegExp()函数返回的结果,标志为非0时,其输出结果是一个数组。
三者的关系应该是这样的:捕获的是表达式匹配的内容,而输出的又是捕获的内容。
当表达式中不存在子表达式捕获组时,三者的内容是一致的,许多简单的正则表达式就是这样。其会将整个表达式匹配的内容进行捕获(其实这也是一个捕获组,编号为0,且总是存在),并将之输出。这导致一些初学者产生了假象,常常以为输出的内容就是匹配的结果,这样就把匹配与输出混同了,为今后错误的产生埋下了祸端。
当表达式中存在子表达式捕获组时,其输出的结果会是子表达式捕获的内容,而非整个表达式匹配的内容。即如果存在编号大于0或有命名的子表达式捕获组时,结果输出总是这些子表达式捕获组捕获的内容。
所以初学者可以这样理解匹配、捕获与输出的关系:三者会同时存在一次正则处理中,如果一个正则表达式中没有普通意义的小括号(子表达式捕获组)时,可以理解为整个表达式的外面有一对小括号,只是这个小括号省略了而已,这个省略的小括号当然起捕获作用(实际上也是存在这个捕获组,其编号为0,是真的省略了),并最后将捕获的内容输出; 如果表达式中存在子表达式捕获组时,处理结果仅是将所有子表达式捕获组捕获的内容进行输出,而不再输出整个表达式匹配的内容。
举例1,帖子见:http://www.autoitx.com/forum.php ... hlight=%D5%FD%D4%F2,发帖者表述如下:
匹配文本: 707-827-7019
正则表达式: ([0-9]{3,4}[.-]?)+
匹配结果: 7019,未达到发帖者的要求。
这里发帖者说“匹配结果”,就说明其未清楚匹配、捕获与输出的概念及三者的关系。准确的说法是“输出结果”或“输出的内容”。发帖者的目的是要输出的内容为707-827-7019,而实际输出的是7019,导致认为正则工具有问题。如果清楚匹配、捕获与输出三者概念及关系,就很容易找到其给出的表达式错误所在。由于其表达式中存在子表达式即捕获组([0-9]{3,4}[.-]?),所以输出的内容就是子表达式捕获组捕获的内容(组的编号为1),即7019。表达式中最后的+号,是表示前面的组重复一次或更多次,而源字符串707-827-7019符合重复三次的条件,所以发帖者给出的表达式是正确匹配了源字符串中其想要匹配的内容,只是输出的不是其想要的内容。详细正则处理过程可表述如下:
源字符串文本: 707-827-7019
正则表达式: ([0-9]{3,4}[.-]?)+
匹配到的内容: 707-827-7019
0号捕获组捕获的内容:707-827-7019
1号捕获组捕获的内容:7019
输出的内容:7019
把源字符串文本及表达式改一下,更能便于理解,假设如下:
源字符串文本: 707-827-7019,310-3479-3431-355
正则表达式: ([0-9]{3,4}[.-]?){4}
匹配到的内容:310-3479-3431-355
0号捕获组捕获的内容:310-3479-3431-355
1号捕获组捕获的内容:355
输出的内容: 355
对发帖者的表达式略作修改,就可以达到发帖者原先的目的。因为其要输出的是整个表达式匹配的内容,所以其表达式中不能有子表达式捕获组,而是要把整个表达式用小括号括起来,以把整个表达式匹配的内容进行捕获并输出,所以应该这样((?:[0-9]{3,4}[.-]?)+),当然最外面的小括号可以省略,即(?:[0-9]{3,4}[.-]?)+,表达式中紧跟左括号的是?:,即(?:exp),意为这是一个不进行捕获的组。另每串数字的特征是3到4位,其后跟一个“-”,最后一串数字未跟“-”,元字符\d等价于[0-9],所以更精简的表达式是(?:\d{3,4}-?)+。 详细的正则处理过程表述如下:
源字符串文本: 707-827-7019
正则表达式: (?:\d{3,4}-?)+
匹配到的内容: 707-827-7019
0号捕获组捕获的内容:707-827-7019
输出的内容:707-827-7019
再如例2:http://www.autoitx.com/forum.php ... hlight=%D5%FD%D4%F2
源字符串文本: <a href=http://xxxxxxxx/xxxxxx.rar…..
想要输出:以http开头和以rar或以zip结尾的字符串内容
发帖者的正则表达式:(http:).*((zip)|(rar))
匹配到的内容:http://xxxxxxxx/xxxxxx.rar
0号捕获组捕获的内容:http://xxxxxxxx/xxxxxx.rar
1号捕获组捕获的内容:http:
2号捕获组捕获的内容:rar
3号捕获组捕获的内容:空
4号捕获组捕获的内容:rar
输出的内容:http:、rar、空、rar,未达到发贴者的目的。
(注意,在只有普通捕获组的情况下,捕获组的编号是按照“(”出现的顺序,从左到右,从1开始进行编号的)
由于其表达式中存在多个子表达式捕获组,所以输出的是多个子表达式捕获组的内容,而不会是0号捕获组捕获的内容即完整的URL地址。所以为了达到发贴者想要的目的,须去掉表达式中的子表达式捕获组,以让整个表达式匹配的内容用于输出。这样又要用上(?:exp)这个表示不捕获分组内容的语法开关,正确的正则表达式可以为:http.+?(?:zip|rar)。详细正则处理过程表述如下:
源字符串文本: <a href=http://xxxxxxxx/xxxxxx. rar…..
正则表达式:http.+?(?:zip|rar)
匹配到的内容:http://xxxxxxxx/xxxxxx.rar
0号捕获组捕获的内容:http://xxxxxxxx/xxxxxx.rar
输出的内容:http://xxxxxxxx/xxxxxx.rar
(第一部分完) |