﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-Java Oracle .net study-文章分类-综合内容</title><link>http://www.cnitblog.com/wangbin/category/6074.html</link><description>主要适用于Web开发的应用</description><language>zh-cn</language><lastBuildDate>Tue, 11 Oct 2011 01:25:04 GMT</lastBuildDate><pubDate>Tue, 11 Oct 2011 01:25:04 GMT</pubDate><ttl>60</ttl><item><title>正则表达式中的Greedy qualifier, Lazy qualifier和Atomic grouping</title><link>http://www.cnitblog.com/wangbin/articles/33080.html</link><dc:creator>王彬</dc:creator><author>王彬</author><pubDate>Fri, 07 Sep 2007 03:45:00 GMT</pubDate><guid>http://www.cnitblog.com/wangbin/articles/33080.html</guid><wfw:comment>http://www.cnitblog.com/wangbin/comments/33080.html</wfw:comment><comments>http://www.cnitblog.com/wangbin/articles/33080.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/wangbin/comments/commentRss/33080.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/wangbin/services/trackbacks/33080.html</trackback:ping><description><![CDATA[ 常用的正则表达式引擎（NFA）大都提供了Greedy qualifier, Lazy qualifier（在Java中称为Reluctant qualifier）和Atomic grouping(在Java中对应的概念是Possessive qualifier)，但开发人员一般用到的只是最普通的，默认的qualifier，许多人甚至根本不了解qualifier的分类。那么，这三个概念的，到底有什么区别呢？ 

 
先来看Greedy qualifier和Lazy qualifier，要了解这两者的区别，必须引入一个概念，就是回溯（Backtracking）。

简单的说，回溯就是记录已匹配的子表达式的可能状态（例如，待匹配串中为字符aa，正则表达式为a+，则回溯机制会记录：a+既可以匹配a，也可以匹配aa，并选取其中之一作为当前尝试），如果接下来的子表达式无法匹配，就需要回溯，更改之前表达式的状态。

 下面我们来看一个回溯的例子：

若待匹配串为

abcdefg6

正则表达式为

^.*\d$

正则引擎会从左至右开始匹配，因为.可以匹配任何字符，则.*会匹配整个字符串abcdefg6（实际上，每前进一个字符，正则引擎都保存了选择信息以供回溯），此时已经前进到待匹配串的末端，而正则式尚未匹配完成，引擎便会启用回溯机制——\d会对.*说，我来了，吃了我的，给我吐出来J——.*于是回退一位，把6交出来，\d匹配完成，最后，换行符与$匹配，则整个正则式的匹配完成。

 了解了回溯，再来看Greedy qualifier和Lazy qualifier就很容易了。

Greedy qualifier之所以得名，是因为它记录下回溯信息之后，总是尝试&#8220;满足&#8221;(match)的选项。举例来说，\d?代表匹配一位或零位数字，Greedy qualifier记录下两个选项之后，首先尝试的便是匹配一位数字。

而Lazy qualifier在记录下回溯信息之后，首先尝试的是&#8220;不满足&#8221;(not match)的选项。仍然举\d?的例子（注意，这里只是为了利用\d?的概念，应用时需要另加修饰符规定\d?为一个Lazy qualifier），如果是Lazy qualifier，则首先尝试&#8220;不匹配数字&#8221;的分支。

 一般来说，Greedy qualifier是默认的选项，我们通常使用的qualifier就是Greedy qualifier。如果需要采用Lazy qualifier，开发人员需要把\d?写作\d??（在Java语言中，Lazy qualifier的标志是在相应的Greedy qualifier之后加上一个?），正则引擎才能正确识别。

 至此，我们可以看一个例子：

The name &#8220;McDonald&#8217;s&#8221; is said &#8220;makudonarudo&#8221; in Japenese

如果使用&#8221;.*&#8221;匹配，则取得&#8220;McDonald&#8217;s&#8221; is said &#8220;makudonarudo&#8221;

如果使用&#8221;.*?&#8221;匹配，则取得&#8220;McDonald&#8217;s&#8221;

 
熟练的使用这两种qualifier，能为开发人员提供极大的便利。

然而，仅有这两种qualifier还是不够的，因为回溯需要记录之前的选择分支，很多时候会影响匹配的效率，来看下面的例子：

正则表达式为

^\w+:

待匹配串为

ThisIsAnInefficientMatch

希望匹配的是以新行为开头，以:结尾的一串字符，我们一眼就能看出，待匹配串不包含这种子串，但正则引擎需要进行多次回溯（在匹配:之前，\w+已经匹配了整行，每个字符之前都保留了一个选择分支）才能得出结果，即便换用^\w+?:，仍然需要进行多次无谓的尝试。

 

正因为如此，正则引擎提供了Atomic Grouping的机制。

简单的说，Atomic Grouping的主要功能便是取消回溯，提高效率——如果匹配成功，它与普通的grouping并无区别，但是如果匹配失败，所有位于Atomic Grouping中的状态会全部失效。

来看上一个例子，如果我们为\w+子串加上Atomic Grouping，写作(?>\w+)，整个正则表达式变为^(?>\w+):，在group内部，\w+是一个greedy qualifier，将会匹配整行字符，并且记录下多个回溯状态（注意，这里的记录都在Atomic Grouping内部），然后开始匹配:，正则引擎发现:无法匹配，此时无需回溯(?>\w+)内部的分支，而是将它们全盘放弃，这样极大地提高了匹配（主要是匹配失败）的效率。

 
一些语言或许没有提供Atomic Grouping的功能，或者以另一种方式提供，例如Java语言引入了Possessive qualifier，其作用与Atomic Grouping类似，只是写法不同。上面的例子中，Atomic Grouping的(?>\+)，用Possessive qualifier，写作\w++。<img src ="http://www.cnitblog.com/wangbin/aggbug/33080.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/wangbin/" target="_blank">王彬</a> 2007-09-07 11:45 <a href="http://www.cnitblog.com/wangbin/articles/33080.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>