当前位置:编程学习 > JS >>

正则表达式的高级技巧分享

答案:正则表达式(regular expression abbr. regex) 功能强大,能够用于在一大串字符里找到所需信息。它利用约定俗成的字符结构表达式来发生作用。不幸的是,简单的正则表达式对于一些高级运用,功能远远不够。若要进行筛选的结构比较复杂,你可能就需要用到高级正则表达式。

本文介绍正则表达式的高级技巧。筛选出了八个常用的概念,并配上实例解析,每个例子都是满足某种复杂要求的简单写法。如果你对正则的基本概念尚缺乏了解,请先阅读这篇文章,或者这个教程,或者维基条目。

这里的正则语法适用于php,与perl兼容。



1. 贪婪/懒惰


所有能多次限定的正则运算符都是贪婪的。他们尽可能多地匹配目标字符串,也就是说匹配结果会尽可能地长。不幸的是,这种做法并不总是我们想要的。因此,我们添加“懒惰”限定符来解决问题。在各个贪婪运算符后添加“?”能让表达式只匹配尽可能短的长度。另外,修改器“u”也能惰化能多次限定的运算符。理解贪婪与懒惰的区别是运用高级正则表达式的基础。

贪婪操作符
操作符 匹配之前的表达式零次或零次以上。它是一个贪婪操作符。请看下面的例子:

复制代码 代码如下:

preg_match( ' /< h1> .< /h1> /' ' < h1> 这是一个标题。< /h1>
< h1> 这是另一个。< /h1> ' $matches )

句点(.)能代表除换行符外的任意字符。上面的正则表达式匹配 h1 标签以及标签内的所有内容。它用句点(.)和星号()来匹配标签内的所有内容。匹配结果如下:

1.< h1> 这是一个标题。< /h1> < h1> 这是另一个。< /h1>
整个字串都被返回。 操作符会连续匹配所有内容—— 甚至包括中间的 h1 闭合标签。因为它是贪婪的,匹配整个字串是符合其利益最大化原则。

懒惰操作符
把上面的式子稍作修改,加上一个问号(?),能让表达式变懒惰:

1./< h1> .?< /h1> /
这样它会觉得,只需匹配到第一个 h1 结尾标签就完成任务了。

另一个有着类似属性的贪婪操作符是 {n } 。它代表之前的匹配模式重复n次或n次以上,如果没有加上问号,它会寻找尽可能多的重复次数,加上的话,则会尽可能少重复(当然也就是“重复n次”最少)。


复制代码 代码如下:

# 建立字串
$str = ' hihihi oops hi'
# 使用贪婪的{n }操作符进行匹配
preg_match( ' /(hi){2 }/' $str $matches ) # matches[0] 将是 ' hihihi'
# 使用堕化了的 {n }? 操作符匹配
preg_match( ' /(hi){2 }?/' $str $matches ) # matches[0] 将是 ' hihi'

2. 回返引用(back referencing)

有什么用?
回返引用(back referencing)一般被翻译成“反向引用”、“后向引用”、“向后引用”,个人觉得“回返引用”更为贴切[笨活儿]。它是在正则表达式内部引用之前捕获到的内容的方法。例如,下面这个简单例子的目的是匹配出引号内部的内容:

复制代码 代码如下:

# 建立匹配数组
$matches = array()

# 建立字串
$str = " " this is a ' string' " "

# 用正则表达式捕捉内容
preg_match( " /(" |' ).?(" |' )/" $str $matches )

# 输出整个匹配字串
echo $matches[0]

它会输出:



1." this is a'
显然,这并不是我们想要的内容。

这个表达式从开头的双引号开始匹配,遭遇单引号之后就错误地结束了匹配。这是因为表达式里说:(”|'),也就是双引号(”)和单引号(')均可。要修正这个问题,你可以用到回返引用。表达式1 2 … 9 是对前面已捕获到的各个子内容的编组序号,能作为对这些编组的“指针”而被引用。在此例中,第一个被匹配的引号就由 1 代表。

如何运用?
将上面的例子中,后面的闭合引号替换为1:

1.preg_match( ' /(" |' ).?1/' $str $matches )
这会正确地返回字串:

1." this is a ' string' "
译注思考题:

如果是中文引号,前引号和后引号不是同一个字符,怎么办?

还记得php函数 preg_replace 吗?其中也有回返引用。只不过我们没有用 1 … 9,而是用了 $1 … $9 … $n (此处任意数目均可)作为回返指针。例如,如果你想把所有的段落标签< p> 都替换成文本:

复制代码 代码如下:

$text = preg_replace( ' /< p> (.?)< /p> /'
" & lt p& gt $1& lt /p& gt " $html )

参数$1是一个回调引用,代表段落标签< p> 内部的文字,并插入到替换后的文本里。这种简便易用的表达式写法为我们提供了一个获取已匹配文字的简单方法,甚至在替换文本时也能使用。

3. 已命名捕获组(named groups)
当在一个表达式内多次用到回调引用时,很容易就把事情搞混淆,要弄清那些数字(1 … 9)都代表哪一个子内容是件很麻烦的事。回调引用的一个替代方法是使用带名字的捕获组(下文简称“有名组”)。有名组使用(?p< name> pattern)来设定,name代表组名,pattern是配合该有名组的正则结构。请看下面的例子:

1./(?p< quote> " |' ).?(?p=quote)/
上式中,quote就是组名,”|' 的是匹配内容的正则。后面的(?p=quote)是在调用组名为quote的有名组。这个式子的效果和上面的回调引用实例一样,只不过是用了有名组来实现。是不是更加易读易懂了?

有名组也能用于处理已匹配内容之数组的内部数据。赋予特定正则的组名也能作为所匹配到的内容在数组内部的索引词。

复制代码 代码如下:

preg_match( ' /(?p< quote> " |' )/' " ' string' " $matches )

# 下面的语句输出“' ”(不包括双引号)
echo $matches[1]

# 使用组名调用,也会输出“' ”
echo $matches[' quote' ]

所以,有名组并不只是让写代码更容易,它也能用于组织代码。

4. 字词边界(word boundaries)

字词边界是字串里的字词字符(包括字母、数字和下划线,自然也包括汉字)和非字词字符之间的位置。其特殊之处就在于,它并不匹配某个实在的字符。它的长度是零。 b 匹配所有字词边界。

不幸的是,字词边界一般都被忽视掉了,大部分人都没有在意他的现实意义。 例如,如果你想要匹配单词“import”:

1./import/
注意了!正则表达式有时候很调皮的。下面的字串也能和上面的式子匹配成功:

1.important
你或许觉得,只要在import前后加上空格,不就可以匹配这个独立的单词了:

1./ import /
那如果遇上这种情况呢:

1.the trader voted for the import
当 import 这个词在字串开头或者结尾时,修改后的表达式仍然不能用。因此,考虑各种情况是必须的:

1./(^import | import | import$)/i
别慌,还没完呢。如果遇到标点符号了呢?就为了满足这一个单词的匹配,你的正则可能就需要这样写:

1./(^import(:| | )? | import(:| | )? | import(.|?|!)?$)/i
对于只匹配一个单词来说,这样做实在是有点大动干戈了。正因如此,字词边界才显得意义重大。要适应上述要求,以及很多其他情况变种,有了字符边界,我们所需写的代码只是:

1./bimportb/
上面所有情况都得到了解决。 b 的灵活性就在于,它是一个没有长度的匹配。它只匹配两个实际字符之间想象出的位置。它检查两个相邻字符是否是一个为单字,另一个为非单字。情况符合,就返回匹配。如果遇到了单词的开头或结尾, b 会把它当成是非单词字符对待。由于import里面的 i 仍然被看成是单词字符,import 就被匹配出来了。

注意,与b相对,我们还有 b,此操作符匹配两个单字或者两个非单字之间的位置。因此,如果你想匹配在某个单词内部的‘hi',可以使用:

1.bhib
“this”、“hight”,都会返回匹配,而“hi there”则会返回不匹配。

5. 最小组团(atomic groups)

最小组团是无捕捉的特殊正则表达式分组。通常用来提高正则表达式的效能,也能用于消除特定匹配。一个最小组团可以用(?> pattern) 来定义,其中pattern是匹配式。

1./(?> his|this)/
当正则引擎针对最小组团进行匹配时,它会跳过组团内标记的回溯位置。以单词“smashing”为例,当用上面的正则表达式匹配时,正则引擎会先尝 试在“smashing”里寻找“his”。显然,找不到任何匹配。此时,最小组团就发挥作用了:正则引擎会放弃所有回溯位置。也就是说,它不会尝试再从 “smashing”里查找“this”。为什么要这样设置?因为“his”都没有返回匹配结果,包含有“his”的“this”当然就更匹配不了了!

上面的例子并没有什么实用性,我们用/t?his?/ 也能达到效果。再看看下面的例

上一个:正则表达式应用之提炼百度歌词的实现代码
下一个:JS正则中的match与exec使用说明

CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,