[文章原始发表:This Is WWW : http://www.plrsoft.cn/blog/?p=69  转载请注明出处]

有一个伟人说过,正则表达式是一个好东西。

不管是哪个伟人说的,今天我要说的是,正则要想快速上手,十分简单!首先给大家推荐一个在线正则工具http://gskinner.com/RegExr/ ,大家可以一边看文章,一遍去检验,很快就会把平时都用到的正则表达式套路学会了。

首先让我们认识正则。正则表达式最初并不是因为计算机的发明而发明的,而是来自于神经网络研究,然后被用于有机化学的分子组成检验,所以,我们可以想象,一个化学家要从一串大小不一的分子中去寻找一系列特定的分子组成或者一个生物学家要从一组DNA长链中寻找一个特定的DNA片段,那该是多么郁闷啊!那么长的东西,你敢用循环去一个一个验证么?那样的话,100芯的、奔七奔八奔万的电脑都不够用!你可以想象,如果用循环去验证,首先你要拆开这组特定分子变成一个一个字符,然后提取出第一个字符去整个分子构成中去寻找匹配的,找到第一个后,记录下位置,并检验这个位置之后的东西是不是跟第二个字符相匹配,然后,第三个,第四个……遇到一个不匹配的了,又从头开始…………那样真的会疯掉的。

所以,我们的伟人发明了伟大的正则表达式。可以从前面对这个郁闷的生物学家的行为知道,他所希望的正则表达式就是这样一个东西:它应该可以整体性的描述我手中这组特定的分子,而不需要拆散成单个分子来逐个匹配,然后用这个描述去整个分子构成中去寻找相符的部分,这就大大提高了速度。注意:整体性是其中的要义!

服了吧?

正则表达式被搬到计算机上来之后,各种不同的编程语言都对它有不同程度的改写,主要是定义方法不一样,提供的函数不一样等等,但是,其基本思想是一样的,个把关键字、个把函数不一样,完全无所谓,我们要学的是——核心!

为了便于演示,我们这篇文章采用JAVASCRIPT的正则表达式来给大家讲解。

开始正则之旅

事实上,我们都曾经不经意的用到过正则并表达式的一些东西。比如我们在一个句子中寻找一些特定字符,并把它替换的情况,都会用到replace函数。

  1.  
  2. myVal = "The authors make no assumptions about the background of the book’s readers other than that you areworking software developers interested in practical solutions to real-world problems. ";  
  3. myVal = myVal.replace("t","c") ;  

 

我们现在想要把所有的t都替换成c,于是我们写出了上面这样一个句子。但是悲剧出现了!我们发现只有The authors 中的t被替换了,其他的t都还活得好好的……

这是为毛呢?难道要我们做一个循环,然后……………………………………替换?那岂不是太找抽了?

经去伟大的谷歌百度一下[教程]正则快速上手指南:1 - 初次相识  _正则,我们都知道了,应该这样写:

 

  1. myVal = myVal.replace(/t/g,"c") ; 

这里面的/t/g,就是一个很简单的正则表达式。在JS中,你可以方便的通过  /…../.. 来定义一个正则表达式。包含在/…/中的就是正则表达式主题,跟在后面的,就是一些设置,比如我们这里用了g,就是global(全局)的意思。这样我们就避免了写那些麻烦的循环语句,而一次性的替换掉所有的t了!好了,问题又来了!当替换后,为毛这个语句变成了这样呢?

The auchors make no assumpcions abouc che background of che book’s readers ocher chan chac you are working sofcware developers inceresced in praccical solucions co real-world problems.

还有一个T,好像被漏掉了?程序出错了么?  NO,这是因为正则表达式默认严格匹配大小写。如果我们不分大小写的替换,那就要加一个i (ignoreCase  忽略大小写),所以我们的整个表达式变成了这样:

  1. myVal = myVal.replace(/t/gi,"c") ; 

有意思吧?

说实话很有点意思!

一步步认识正则表达式关键字(元字符)和限定符:

跟所有的独立成一体的编程语言一样,正则表达式有他自己的关键字,我们一般称为“元字符”。虽然正则表达式本身提供了非常多的语法,但是,但是,但是,常用的真的不多,就几个,希望大家在练习中加以牢记:

常用的元字符
元字符 功能
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束

下面我会用一些好玩的案例告诉他家怎么用他们,并逐步介绍一些限定符给大家。

比方说,作为一个搞WEB技术的人才,你现在牛逼了,终于开始用正则表达式了,你会去找点什么乐子来练习练习?好吧,我觉得你会跟我一样,会拿一段HTML代码来开刀,因为,我知道,你曾经太痛苦了!那么,来吧,我们一起来结束这段痛苦!

我们就用这一段简约而不简单的代码来开刀:

<span style=”color:Red”>作为一个搞<a href=”xxx1″>WEB技术</a>的人才,你现在牛逼了<img src=”./p_w_picpaths/xxx.jpg” />,终于开始用  <a href=”xxx2″>正则表达式了</a></span><img src=”./p_w_picpaths/xxxxxxxx.jpg” />

现在,我想做的第一件事,就是将这个里面的

<img src=”./p_w_picpaths/xxx.jpg” />这类<img …>标签剔除掉,怎么搞?很简单,最简单最笨的办法就是

 

  1.  
  2. myVal=myVal.replace("\<img src=\"\.\/p_w_picpaths\/xxx.jpg\" \/>,","");  
  3. myVal=myVal.replace("\<img src=\"\.\/p_w_picpaths\/xxxxxxxx.jpg\" \/>,","");       

如果你是这样干的,那么恭喜你,你还保持着朴素的本色!因为,这样的确能够替换掉,但是,如果里面的图片地址,不是xxx.jpg呢?他换了一个名字呢?如果有更多个<img> 呢?你要一个一个的replace么? NO! 你要做的,不是剔除掉某张特定的图片,而是剔除掉任何<img>标签。所以,来让我们做一个匹配!我将匹配过程写在下面——

要匹配的语句模型:<img src=”./p_w_picpaths/xxx.jpg” />,<img src=”./p_w_picpaths/xxxxxxxx.jpg” />

构建匹配的逻辑:   从左到右,先遇到“<img”,保留、遇到空格,有可能是1个或者好几个,想个办法来描述这些空格、遇到src后面跟着一串东西,也要像个办法来统一描述这些东西、最后遇到“ />”,保留。OVER。

生成正则并表达式: 根据以上三个步骤,表达式就是   /<img\s*[^>]*>/gi

好的,现在让我们来解释一下:

/                #这个/告诉我们,正则表达式要开始了!

<img        #这是我们要匹配的标签,原原本本的写下来

\s*           #上面说过了,\s 匹配任意的空白符 *告诉系统,如果出现0~无穷多次空白符,都是可以的,因为这里我们不知道会遇到几个空格,有些代码风格差的同学可能会来上一大串空格

[^>]*      #这句话告诉系统,接下来要匹配的0~无穷多个字符,都不能是 “>”符号,如果遇到“>”符号则匹配结束,否则继续。

>              #这句话告诉系统,要匹配一个“>”符号。

/gi             #这句话告诉系统,正则表达式匹配结束,全局模式、且忽略大小写。也就是匹配所有<img>和<IMG>标签

我想,现在你应该会写一些基本的匹配了,因为,他完全可以从左到右的像自然语言一样方便的来描述一个东西,然后转化成正则表达式即可!!

事实上,我们写成这样,也是可以正常匹配的!   —— /<img.*?[^>]*>/gi 比方才那个看起来更简单,是不是(很多人都怕“\”这个符号哟……我也怕,看得头晕,但事实上它只是一个转义字符,也就是说,为了避免我们要匹配一些关键字,又怕系统认出他是关键字从而出现语法错误的时候,就用万能的转义字符”\”,比如我们要查询“.”,我们就用“\.”,要匹配括号“(”,就用“\(”,这个不再多说) ?请自己思考为什么!

上面,我们用到了一些限定符,下面也给大家介绍一下:

{n,m}        匹配前面的一个或者一组(通过[]和()限定组别)字符n到m次,其中m可以省略。比如,/c{1,4}/  ,可以匹配c、cc、ccc、cccc。/c{1,}/可以匹配大于一个c的字符到无穷多,/c{3}/只能匹配ccc。

*                这个符号很勤劳,它完全等于{0,},也就是0到无穷多个。  /c*/ ==/c{0,}/

?                这个符号很懒惰,能不匹配尽量不匹配,要匹配最多也匹配1次,因此,它等于 {0,1}

+                这个符号只比*懒惰一点点,他全等于{1,},也就是,必须要匹配一次。

因为“?”这娃子太懒惰了,我们又给了它一些锻炼的机会,允许她傍大款,这个,叫做懒惰限定符——
*?              重复任意次,但尽可能少重复
+?              重复1次或更多次,但尽可能少重复
??               重复0次或1次,但尽可能少重复
{n,m}?       重复n到m次,但尽可能少重复
{n,}?          重复n次以上,但尽可能少重复

我们上面写的 /<img.*?[^>]*>/gi 就是使用了懒惰限定符*?,你可以试试看把“?”去掉,变成/<img.*[^>]*>/gi ,那么你将得到一个惊喜:你把“<img”后的所有内容都被匹配了!素以,偶尔的懒惰害死很好的,不要看不起“?”!

现在我们再回到刚才这个img标签的过滤问题中来,我们发现不管是/<img\s*[^>]*>/gi  还是 /<img.*?[^>]*>/gi ,我们都给了一个东西很大的面子,谁?空格!

因为我们刚才是按照要匹配的语句模型:<img src=”./p_w_picpaths/xxx.jpg” />来构想我们的正则表达式的,这个模型翻译出来就是  “ <img “ + 数量不明的空白+” src= ” +’  ‘ +” ./p_w_picpaths/xxx.jpg “+’ ‘+”  /> “,

正则表达式和毛主席都告诉我们,要抓住主要矛盾!我们要透过现象看本质!其实,我们再进一步压缩这个正则表达式,还是可行的,比如压缩成这样 : /<img[^>]*>/gi 看,是不是简单大方又实用?

为什么可以这样写呢?

事实上,这种写法正是应了那句:抓住主要矛盾!我们回想一下,我们要剔除什么?对了,要剔除的是<img>标签和这个标签内的东西,至于标签里面包含了啥,管他src属性也好,alt属性也好,也不管理图片路径多长,这些我都不关心,我只需要用一个东西来替代就可以了,事实上,可以有很多东西替代它:

1、如果IMG标签的 src和alt属性中不包含中文字符,我们可以用 \w 来描述他们

2、如果IMG里面包含很多空格,我们可以用 \s 来描述他们

3、如果还可能出现一些古里八怪的字符,我们可以用 . 来替代他们

但是,我是一个很节省的人,看着很长的东西我就惊慌,所以我要这个正则表达式尽量的短,越短,匹配的速度就越快。所以,诞生了 /<img[^>]*>/gi

这个简短的正则中,最关键的部分无疑就是 [^>] ,他是什么呢?中括号是啥意思呢?请看下一节:神奇的分组。