正则表达式分组、引用和断言
这几日看权威指南,对正则里的分组、引用和断言有了更深的理解,特地整理一下加深印象。
为了详细地解释,首先将权威指南第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']