找回密码
 加入
搜索
查看: 19179|回复: 22

[原创] 正则快速入门之一——匹配、捕获与输出

 火.. [复制链接]
发表于 2014-8-4 10:03:05 | 显示全部楼层 |阅读模式
本帖最后由 shqf 于 2014-8-4 10:06 编辑

作者shqf 2014-8-4 0:30

前言
  一直看到论坛的一些兄弟在求正则,再看这些正则要求,往往是一些常见的应用。从这些发贴者给出的自己写的正则表达式看,似乎这些发贴者也已看过一些正则的教程,知道不少正则的知识。但是一到实际运用时,写出的正则表达式总难以达到要求。这是由于这些发贴者对一些概念的不甚清楚,造成很难把掌握到的知识发在实际运用中去发挥出来。看着许多兄弟在正则殿堂的大门口徘徊,而迟迟不能入门的情形,就一直想写篇自己入门的感悟来帮助这些初学者。
  论坛中有许多正则的高手。我经常看他们的文章、帖子汲取正则的营养。目前我对正则的掌握也只能算入门不久吧。而我却还要想写还这样一篇文章,一是受一些论坛正则高手乐于助人之精神的影响,二可以说是初生的牛犊不怕虎吧,三是因为刚入门不久,学习过程中获得的一些体会尚清晰,整理一下也便于自己提高吧。总之差错肯定难免,敬请论坛的各位高手批评指正。
本文不敢妄称教程,只能算是自己学习正则入门的一点体会感悟,从实际运用的角度进行讲解,例子均出自本论坛。适合看过一些正则教程,略知一些正则知识,但不太会实际运用的正则初学者,没有一些正则的基本知识或想要在正则上晋阶、登堂入室者,请绕过吧。
  在讲解之前,对初学者容易分辨不清的、搞混的一些基本的正则知识有必要进行啰嗦一下:
  首先是小括号(  )。其作用是表示捕获与分组,注意其捕获与分组的作用是同时存在的,有的教程中直接称之为捕获组。一些初学者只记住其有分组作用,而忽略其捕获作用,导致写出的表达式达不到效果。关于捕获组,后文还会详细讲到。我将这样的小括号称之为普通意义的小括号。
如果小括号中紧跟左括号后面是一个问号?,形如(?   ),则这些小括号已有其他意义,不再有捕获或分组的作用。具体运用时,要查有关教程,不可模糊。如(?i)、(?s)、(?m)等已完全没有捕获或分组的作用。(?:  )、(?=  )、(?<=  )、(?!   )、 (?<!   )等等虽仍有分组作用,但不再有捕获作用,所以没有组号或组名。(注意,唯一的一个例外是(?<name>Expression),虽然左括号后面也紧跟一个问号,但仍有捕获与分组作用,是命名捕获组,为了理解或记忆的方便,可以暂时不用去细究。)
  其次是问号?。作用1是如上所述,紧跟在左括号后面,是某些开关的标志,表明小括号已不再有捕获或分组的作用,即已不是原来的小括号了,其已有特殊的意义了。2是属于限定符(此概念及本文以下用到的一些正则特有的概念主要出自《正则表达式30分钟入门教程》,作者deerchao),常用在元字符、普通字符(或组)后面,表示前面的字符(或组)重复0次或1次,与*、+属于同类。3是用在限定符的后面,所谓懒惰模式,如*?、+?、??、{n,m}?、{n,}?,表示最少重复的次数。
  另,学习正则最好使用正则调试工具多进行测试。我常用的是AutoIt中文论坛版主Afan写的正则助手Au3.REHelper,有意者可至论坛下载。

评分

参与人数 4威望 +5 金钱 +230 贡献 +21 收起 理由
xms77 + 50 + 5 又一篇正则学习的美文,顶死!
user3000 + 50 + 11 +++Good job!
fuldho + 30 精品
afan + 5 + 100 + 5 精品文章

查看全部评分

 楼主| 发表于 2014-8-4 10:04:01 | 显示全部楼层
本帖最后由 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
  (第一部分完)
发表于 2014-8-4 13:49:12 | 显示全部楼层
回复 1# shqf


受教了!
继续等待拜读.........
发表于 2014-8-4 17:30:24 | 显示全部楼层
这个帖子很好  期待下文
发表于 2014-8-5 06:44:47 | 显示全部楼层
很好  期待下文
发表于 2014-8-5 09:16:13 | 显示全部楼层
这个帖子太好了  期待下文,支持LZ
谢谢
发表于 2014-8-6 10:21:23 | 显示全部楼层
这个帖子太好了  期待下文........................
发表于 2014-8-9 16:56:05 | 显示全部楼层
很受用,谢谢。
我能学到新的是:(?:exp)这个表示不捕获分组内容
发表于 2014-8-10 10:07:34 | 显示全部楼层
学习了,学习了,学习了
发表于 2014-8-10 14:02:07 | 显示全部楼层
支持楼主,,
发表于 2014-8-10 22:37:45 | 显示全部楼层
真的又学到了原来不知道的正则知识,感谢楼主的详解,期待后续的篇章!
发表于 2014-8-11 09:24:22 | 显示全部楼层
学习一下,谢谢
发表于 2014-9-19 09:40:43 | 显示全部楼层
期待下文,支持LZ
发表于 2014-12-19 12:06:59 | 显示全部楼层
期待楼主的下文。。。
发表于 2014-12-19 16:58:41 | 显示全部楼层
等待中,楼主加油
您需要登录后才可以回帖 登录 | 加入

本版积分规则

QQ|手机版|小黑屋|AUTOIT CN ( 鲁ICP备19019924号-1 )谷歌 百度

GMT+8, 2024-4-19 18:40 , Processed in 0.093811 second(s), 23 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表