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

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

为了详细地解释,首先将权威指南第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.
(?= )A positive lookahead assertion. Require that the following characters match the pattern p, but do not include
those characters in the match.
(?! )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 = /[a-z]+\d+/; //未进行分组
var reg2 = /([a-z]+)\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指的是带圆括号的子表达式在正则表达式中的位置。因为子表达式是可以嵌套的,所以它的位置是参与计数的左括号的位置。注意:此处的引用并不是对子表达式模式的引用,而是指与那个模式相匹配的文本的引用。

也就是说/([a-z]+)\d\1/如果对"abc123"进行匹配,实际上可以看做是/([a-z]+)\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,截止到(?!\#)匹配的是“右边不是#”的位置,也就是#的右边,从这里开始匹配后边的表达式。第二个表达式也是按同样的方式理解。

 

补充:

  谢谢一楼提醒,这里补充一下es6新增的后行断言

其实也很好理解:

  代码如下

  

var str = '#username'

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

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

// 后行否定断言

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

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

 

 

 以上是我对正则里这几个概念的理解,可能不是很准确,若有错误欢迎指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值