前言
本文介绍一些正则中的常用名词以及对应概念,譬如字符组,捕获组、非捕获组、反向引用、转义和\s
\b
等
大纲
- 字符组@H_301_12@
- 捕获组@H_301_12@
- 反向引用@H_301_12@
- 非捕获组@H_301_12@
-
.
、\s
和\S
@H_301_12@ \b
@H_301_12@-
\
转义@H_301_12@
字符组
[]
字符组表示在同一个位置可能出现的各种字符,也就是说它的匹配结果只能是一个字符,不能是多个
例如[hello]
匹配的不是hello
而是h或e或l或o
特点
示例
特殊字符无需转义
[\^*\-+|(a)]
这个示例的含义是,匹配以下字符中的任意一个
连字符的作用
[z-a]
匹配这个正则表达式会报错(Range out of order in character class
)
原因是连字符后面的字符码要大于等于前面的字符码
var str = 'abc-'; var reg = /[a-z-]/g; // 最后的连字符没有构成区间,所以仅仅表示 - 这个字符 str.match(reg); // ["a","b","c","-"]
var str = "aAbc-"; var reg = /[A-a-z]/g; // 通用,A-a 构成一个区间 之后的 -z 无法构成区间,于是它只能作为-字符本身 str.match(reg); // ["a","A","-"]
排除型
var str = '01ABabc-'; var reg = /[^a-z]/g; // 匹配了除a-z范围外的任意字符 str.match(reg); // ["0","1","B","-"]
排除型字符组用法和普通的一样,但是唯一区别是
\b
特殊情况
var str = 'ab\bd,cdef'; var reg = /[\b]/g; var reg2 = /\bc/g; var reg3 = /[\b]c/g; str.match(reg); // [""] 匹配\b本身 str.match(reg2); // ["c"] 即匹配,号后面的c str.match(reg3); // null 因为\b后面没有c
捕获组
捕获组就是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用
例如: /(a)(b)(c)/
中的捕获组编号为
其中,组0
是正则表达式整体匹配结果,组1
`2`3
才是子表达式匹配结果
特点
- 子表达式捕获组编号从
1
开始,顺序从左到右(例如编号1
是左侧第一个()
包裹的子表达式的结果)@H_301_12@ - 可以在正则表达式中对前面捕获的内容进行引用(反向引用)@H_301_12@
- 也可以在程序中,对捕获组捕获的内容进行引用(比如
replace
中)@H_301_12@
示例
子表达式捕获组编号从1开始,顺序从左到右
var str = "2017-07-29"; var reg = /(\d{4})-(\d{2})-(\d{2})/; // 非全局模式有捕获组结果 str.match(reg); // ["2017-07-29","2017","07","29",index: 0,input: "2017-07-29"]
上述匹配中,除了整个正则表达式的结果外,还有各个捕获组的结果,其中,子表达式的捕获组从编号1
开始,如下
编号 | 捕获组 | 匹配内容 |
---|---|---|
0 | (d{4})-(d{2})-(d{2}) | 2017-07-29 |
1 | d{4} | 2017 |
2 | d{2} | 07 |
3 | d{2} | 29 |
JS程序内的引用
var str = '<div id="code1" class="highlight"></div>'; var reg = /<(\w+)[^>]*>/g; // <div></div> str = str.replace(reg,"<$1>");
可以看到,在去除div
中的属性时,先是整个匹配<divxxx>
,然后再把整个内容替换成<$1>
,其中$1
就是第一个捕获组结果div
的引用
注,请不要引用$0
,因为它不属于子表达式的捕获组,在replace
中引用$0
没有任何效果
var str = 'abcd0123ABCD'; var reg = /([a-z]+)(\d+)([A-Z]+)/g; reg.test(str); // true console.log(RegExp.$0); // undefined console.log(RegExp.$1); // abcd console.log(RegExp.$2); // 0123 console.log(RegExp.$3); // ABCD
注,同样$0
的引用没有内容
反向引用
在正则表达式内部对捕获组进行引用称之为反向引用
var str = "boom==boom"; var reg = /(boom)==\1/; str.match(reg); // ["boom==boom","boom",input: "boom==boom"]
可以看到,正则中\1
的值就是捕获组1
匹配到的结果boom
因此,这个表达式等价于(boom)==boom
示例
var str = '1234567899'; var str2 = '12345678999'; var reg = /^(?:([0-9])(?!\1{2})){1,}$/; reg.test(str); // true reg.test(str2); // false
上例中的效果是,匹配一个数字,但是数字中不允许连续出现3
次以上的重复数字
- 用到反向引用可以很好的实现@H_301_12@
非捕获组
但是有时候,因为特殊原因用到了()
,但又没有引用它的必要,这时候就可以用非捕获组声明,防止它作为捕获组,降低内存浪费
-
?:
可以声明一个子表达式为非捕获组@H_301_12@
var str = 'abcd0123ABCD'; var reg = /(?:[a-z]+)(\d+)([A-Z]+)/g; reg.test(str); // true console.log(RegExp.$0); // undefined console.log(RegExp.$1); // 0123 console.log(RegExp.$2); // ABCD
可以看到,(?:[a-z]+)
将这个子表达式声明成了非捕获组,因此捕获组的编号直接跳过了它,从下一个(\d+)
开始
.
、\s
和\S
首先说下.
再说说\s
与\S
那如何匹配所有字符呢?
示例
请写一个表达式,去除多行注释
var str = '\ var a = 1; \r\n\ /\** \r\n\ * 这里是注释 \r\n\ */\r\n\ var b = 2;\r\n\ console.log(a)'; var reg = xxx; str = str.replace(reg,'');
解答
var reg = /\/\*{1,}[\s\S]*\*\//g;
这里就用的了用[\s\S]
来匹配所有的字符(因为仅仅是.
是无法匹配\r\n
的)
\b
单词边界
\b
匹配单词边界,不匹配任何字符
简单的说,\b
匹配的位置,一侧是构成单词的字符,另一侧是非单词字符,字符串的开始或结束
而其中单词的判断就是\w
的匹配范围(正常a-zA-Z0-9_
,JS
举例)
注,有一特例,在字符组中[\b]
表示的是退格符
特点
示例
var str = 'abc_d=efg+hij哈opq%'; var reg = /.\b./g; // ["d=","g+","j哈","q%"] str.match(reg);
可以看到,分别在如下几处有单词分界
\
转义
var str = 'ab\\cd'; // 字符串 ab\cd var str2 = 'ab\\\\cd'; // 字符串 ab\\cd var reg = /ab\\\\cd/; var reg2 = new RegExp('ab\\\\cd'); reg.test(str); // false reg.test(str2); // true reg2.test(str); // true reg2.test(str2); // false
出现\
的地方,得多加注意,需要梳理清楚转义逻辑,通过上例可以看到,在正则中出现的\
和在字符串中出现的\
意义不一样
-
reg
中,\
出现在正则中,所以\\\\
的意思就是匹配\\
字符串,所以测试str2
通过,str
失败@H_301_12@ -
reg2
中,\
出现在字符串中,所以\\\\
的意思\\
,然后构建为正则表达式,最终在正则中是\\
,也就是匹配\
字符串本身,所以测试str
通过,str2
失败@H_301_12@
附录
博客
初次发布2017.07.31
于个人博客
http://www.dailichun.com/2017/08/01/regularExpressionConcepts.html
参考资料
- 正则表达式元字符@H_301_12@
- 正则表达式基础系列博客@H_301_12@