| 
 
| 赞 | 0 |  
| VIP | 2 |  
| 好人卡 | 27 |  
| 积分 | 1 |  
| 经验 | 26327 |  
| 最后登录 | 2019-10-13 |  
| 在线时间 | 953 小时 |  
 Lv1.梦旅人 
	梦石0 星屑120 在线时间953 小时注册时间2007-4-25帖子805 | 
| Ruby 1.9 使用了一个效率更高、功能更强大的正则表达式引擎——Oniguruma(鬼車)。相比于 Ruby 1.8 的正则表达式引擎来说,Oniguruma 所能描述的再也不仅仅是正则语言了。这其中的一个重要因素是由于递归表达式的出现——正则语言的一大特点就是没有递归。和大多数现代化的引擎一样,Oniguruma 甚至能用来描述上下文无关语言这样的形式语言。可是,“正则表达式”这个术语仍然被各种语言持续使用了下来。可能人们对正则表达式太熟悉了,熟悉到会觉得其名称改为“形式表达式”很别扭。 
 本帖主要介绍一个新功能:命名捕获组。
 
 对于能熟练使用正则表达式的朋友来说,捕获组并不陌生。未经转义的圆括号中的表达式就是一个捕获组,我们之后可以在表达式中引用这个捕获组。传统的引用方式是通过所谓的后向引用,即 \n 的形式,来引用在表达式中从左到右第 n 个出现的捕获组。
 
 这种方式有很多弊端。首先,用户必须维护 1, 2, ..., n 这些数字。一旦发生两个捕获组换位的情况,就要把所有的它们的后向引用都改掉,这对于较长的表达式来说无疑是力气活。如果我们能给捕获组命名,那么即便它的位置变了,名字却不会变,所以无须修改其引用处。
 
 其次,对于代码阅读者来说(把整个表达式背下来的除外),一个数字只不过是一个密码,它本身并不能让读者直接回想起任何已知事物,读者只能从头开始阅读表达式,并按出现顺序找到对应的捕获组,这属于一个简单的破译过程。如果我们给捕获组命名,那么就可以通过一两个单词来简短地描述该捕获组所接受的语言。比如:将一个捕获组命名为“alphanum”,那么读者大概就能猜到这个捕获组匹配的是“alphanumeric”的值,也就是字母或数字。
 
 Oniguruma 命名捕获组是通过 (?<name>regex) 的语法来完成的。name 是捕获组的名称,regex 是捕获组内部的正则表达式。
 如果我们在命名捕获组时配合正则表达式的“忽略空白符”选项(字面值后添加 `x' 后缀),以及在捕获组子表达式后添加 {0} 表示匹配 0 次,就可以大大地提高表达式的可读性。给一个比较复杂的例子,是我之前写的一个匹配电子邮件地址的雏形:复制代码
#coding: GBK
regex = /(?<hex>[0-9a-fA-F])\+\g<hex>/
str = '我们需要取出4+f这个子串!'
p str[regex] # "4+f"
这里,由于使用的是 %r 来构建正则表达式,所以其内部可以直接写普通的注释。每个子表达式后面跟上 {0},表示这里仅仅是定义并命名一个捕获组,并不参与实际匹配。再加上最后的 `x' 后缀,表达式中的空白都会被忽略,所以可以对表达式进行缩进。这个例子就大量使用了命名捕获组,将匹配不同字符串片段的表达式分划到了不同的子表达式。这在便于维护的同时也提高了可读性,和代码模块化是一个道理。复制代码#coding: GBK
# local@domain
$pattern = %r<
# Define subpatterns.
(?<local>       \g<local_t>|\g<quoted>              ) {0}
(?<local_t>     \g<local_a>+(?:\.\g<local_a>+)*     ) {0}
(?<local_a>     \g<alphanum>|[!\#$\%&'*/=?^_`{|}~-] ) {0}
(?<alphanum>    [a-zA-Z0-9]                         ) {0}
(?<quoted>      ".*"                                ) {0}
(?<domain>      \g<hostname>|\[\g<ip_addr>\]        ) {0}
(?<hostname>    \g<label>(?:\.\g<label>)*           ) {0}
(?<label>       \g<alphanum>+(?:-+\g<alphanum>+)*   ) {0}
(?<ip_addr>     \g<zero_255>(?:\.\g<zero_255>){3}   ) {0}
(?<zero_255>    \d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]   ) {0}
# The root pattern is ...
\g<local>@\g<domain>
>x
 http://szsu.wordpress.com/2010/11/13/regex_email_addr/
 | 
 |