Bootstrap

正则表达式分组详解

正则表达式分组、引用和断言

这几日看权威指南,对正则里的分组、引用和断言有了更深的理解,特地整理一下加深印象。

为了详细地解释,首先将权威指南第6版上相关描述的原文贴出来,重点用红色标识。

字符含义
(......)Grouping . Group items into a single unit that can be used with *, +, ?, | , and so on. Also remember the characters 
that match this group for use with later references.
(?:...)Grouping only . Group items into a single unit, but do not remember the characters that match this group.
(?= p )A positive lookahead assertion. Require that the following characters match the pattern p , but do not include 
those characters in the match .
(?! p )A negative lookahead assertion. Require that the following characters do not match the pattern p .

一、分组

(......)有多种作用,一种是把单独的项进行组合,将括号内的项作为一个独立的单元来处理(使用*, +, ?, | , etc),举个例子:

复制代码

//?表示匹配前一项0次或1次
//\s是指任何Unicode空白字符
var reg = /hello(\sworld)?/; //如果world是一个可选项,采用这种方式就可以进行选择
var str1= "hello"; 
var str2 = "hello world"; 
reg.test(str1); //true
 reg.test(str2); //true

复制代码

另一个作用就是在完整的模式中定义子模式,这样我们可以将每个圆括号中子模式匹配出来的结果提取出来,上例子:

复制代码

var str = "abc123" ; //我们匹配的是一个或多个字母后面加一个或多个数字,但是实际上我们只对其中的字母感兴趣
 var reg1 = /[az]+\d+/ ; / /未进行分组
 var reg2 = /([az]+)\d+/ ;
alert(str.match(reg1)); // abc123;只会输出整个匹配正确的字符 
alert(str.match(reg2)); // abc123,abc;子表达式匹配的结果也输出 
alert(str. match(reg2)[1]); // abc;通过[]的选取方式将所需内容提取出来,如果用firebug控制台可以看到相应的index

复制代码

如果对match的用法不了解请参考http://www.w3school.com.cn/jsref/jsref_match.asp 

二、选择

(......)还有个作用就是允许在同一表达式的后部引用前面的子表达式,通过"\n"的方式实现,这里的n指的是带圆括号的子表达式在正则表达式中的位置。因为子表达式是可以嵌套的,所以它的位置是参与计数的左括号的位置。注意:此处的引用并不是对子表达式模式的引用,而是指与那个模式相匹配的文本的引用。

也就是说/([az]+)\d\1/如果对"abc123"进行匹配,实际上可以看做是/([az]+)\dabc/这样一种形式。

这里举个书上的例子:

/['"][^'"]*['"]/; // 这个表达式匹配的是位于单引号或双引号内的任意个字符,但是并不要求左侧和右侧的引号相匹配
// 如果要左右两边的引号是匹配的,可以这样写: 
/(['"])[^'"]*\1/; // 如果(['"])匹配到的是',那么\ 1就是\',这样能保证两边都是一样的符号

 上面为什么要说参与计数的左括号呢?正是因为还有不参与的家伙在,就是(?:),在前面的表格也说了,它是grouping only,就是说它只组合,至于匹配的字符它不记忆,更谈不上分组了,比如说/(?:hello)\s(world)/,这里\1引用的就是与(world)相匹配的字符。

三、断言

断言,就是指明某个字符串前边或者后边,将会出现满足某种规律的字符串。以我的理解,比如说:我要找红色的苹果,这个红色的就是一种断言,虽然我要找是苹果,但是它首先得是红色的。

js中只有先行断言,(?=)是零宽正向先行断言,要求接下来的字符都与p匹配,但不能包括匹配p的那些字符;(?!)是零宽负向先行断言,要求接下来的字符不与p匹配。

先来看一下(?= p )是怎么工作的,/Windows (?=95|98|NT|2000)/能匹配"Windows2000"中的"Windows" ,但不能匹配"Windows3.1"中的" Windows"。

也就是说,它要求Windows后面必须是95|98|NT|2000中的一个,而在匹配正确之后,它的返回值中并不包括95|98|NT|2000的部分:

var reg = /Windows(?=95|98|NT|2000)/ ;
 var str1 = "Windows2000" ;
 var str2 = "Windows3.1" ;
alert(str1.match(reg)); // Windows 
reg.test(str1); // true 
reg.test(str2); // false

   这个还是很好理解的,但是我想试一下的时候就遇到问题了。

// 这里我想匹配类似#userName的结构,也就是/\#\w+/这样的形式;
// 但是呢,我只想提取出userName这一部分,当然这用分组就可以实现了,不过这里还是用这个举下例子
// 首先我是这样写的
var reg = /(?=\#)\w+/ ;
console.log(reg.test( "#cccc")); // false

为啥会是false呢,这就是零宽的概念了,就是只匹配位置,在匹配过程中,不占用字符,所以被称为“零宽”,这种方式也叫非获取性匹配,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始

在上面的例子中,截止到(?=\#)匹配到的是“右边是#的位置”,也就是#的左边,接下来并不是从u开始匹配,而是从#开始,#当然不是\w了所以结果是false,换成/(?=\#)\#w+/结果就是true了。

关于(?!)再举个例子:

复制代码

var str = "#www#eee" ;
 var reg1 = /(?!\#)\w+/ g;
 var reg2 = /\w+(?!\#)/ g;

console.log(str.match(reg1)); // ["www", "eee"] 

console.log(str.match(reg2)); // ["ww", "eee"]

复制代码

解释:

/(?!\#)\w+/g,截止到(?!\#)匹配的是“右边不是#”的位置,也就是#的右边,从这里开始匹配后边的表达式。第二个表达式也是按同样的方式理解。

 

  代码如下

  

复制代码

var str = '#username'

// 后行断言
var reg1 = /(?<=#)\w+/

str.match(reg1) // ['username']

// 后行否定断言

var reg2 = /(?<!#)\w+/

str.match(reg2) // ['sername']

复制代码

;