-
字面量字符和元字符
大部分字符在正则表达式中,就是字面的含义,比如 /a/ 匹配 a , 就像这种,字符只表示它字面的含义,那么它们就叫做”字面量字符”
/dog/.test(‘old dog’) // true
除了字面量字符以外,还有一部分字符有特殊含义,不代表字面的意思,被称为元字符,主要有以下几个:
(1) 点字符 (.)
点字符匹配 除了回车(\r) 换行(\n) 分隔符(\u2028) 和段分隔符 (\u2029) 以外的所有字符.但是对于码点大于0xFFFF字符,点字符不能正确匹配,会认为是两个字符.
(2) 位置字符
位置字符用来提示字符所处的位置,主要有两个字符
-
^ 表示字符串的开始位置
-
$ 表示字符串的结束位置
/^text/.test(’text123’) // true /text$/.test(‘123text’) // true /^text$/.test(’text’) // true
(3) 选择符(|)
在正则表达式中表示”或关系"(OR),也就是 cat|dog 表示匹配cat 或者 dog
其它的元字符还包括
\ \* + ? () [] {} 等
-
-
转义字符
正则表达式中对于上面那些元字符来说,如果匹配它们本身,就需要在它们前面加上反斜杠.比如要匹配+ 就要写成 \+
/1+1/.test('1+1') //false /1\+1/.test('1+1') //true
正则表达式中,需要反斜杠转义的,一共有12个字符:
^
、.
、[
、$
、(
、)
、|
、*
、+
、?
、{
和\
。需要特别注意的是,如果使用RegExp
方法生成正则对象,转义需要使用两个斜杠,因为字符串内部会先转义一次。 -
特殊字符
正则表达式对一些不能打印的特殊字符,提供了表达方法。
\cX
表示Ctrl-[X]
,其中的X
是A-Z之中任一个英文字母,用来匹配控制字符。[\b]
匹配退格键(U+0008),不要与\b
混淆。\n
匹配换行键。\r
匹配回车键。\t
匹配制表符 tab(U+0009)。\v
匹配垂直制表符(U+000B)。\f
匹配换页符(U+000C)。\0
匹配null
字符(U+0000)。\xhh
匹配一个以两位十六进制数(\x00
-\xFF
)表示的字符。\uhhhh
匹配一个以四位十六进制数(\u0000
-\uFFFF
)表示的 Unicode 字符。
-
字符类
字符类 (class) 表示有一系列字符可供选择,只要匹配其中一个就可以了,所有可供选择的字符都放在方括号内,比如 [xyz] 表示x,y,z 任何一个匹配上就行.
/[abc]/.test('hello world') //false /[abc]/.test('apple') //true
有两个字符在字符类中有特殊含义
(1) 脱字符 (^)
如果[ ] 内的第一个字符是 ^,则表示除了字符类这中的字符,其它字符都可以匹配. 比如
[^xyz] 表示除了 x y z 之外都算匹配成功
/[^abc]/.test('bbc news') //true //bbc news 包含除了abc之外的字符,所以匹配成功 /[^abc]/.test('bbc') //false //bbc 中不包含除了abc之外的字符,匹配失败
如果[ ]内没有其它字符 ,只有[^],就表示匹配一切字符,包括换行符,相比之下,点字符是不能匹配换行符的.
另外 脱字符(^)只有在字符类的第一个位置才有特殊含义,否则就是字面含义.
(2)连字符(-)
某些情况下,对于连续序列的字符,连字符(
-
)用来提供简写形式,表示字符的连续范围。比如,[abc]
可以写成[a-c]
,[0123456789]
可以写成[0-9]
,同理[A-Z]
表示26个大写字母。/a-z/.test('b') // false /[a-z]/.test('b') // true
上面代码中,当连字号(dash)不出现在方括号之中,就不具备简写的作用,只代表字面的含义,所以不匹配字符
b
。只有当连字号用在方括号之中,才表示连续的字符序列。 -
预定义模式
指的是某些常见模式的简写方式.
- \d 匹配0-9之间的任一数字 相当于[0-9]
- \D 匹配所有0-9以外的字符 相当于[ ^0-9]
- \w 匹配任意的字母/数字和下划线,相当于 [A-Za-z0-9_]
- \W 匹配除了字母数字下划线以外的字符 相当于[ ^A-Za-z0-9_]
- \s 匹配空格(包括换行符 制表符 空格符等),相当于[ \t\r\n\v\f]
- \S 匹配非空格字符 相当于[ ^ \t\r\n\v\f]
- \b 匹配词的边界
- \B 匹配词的非边界,即在词的内部
-
重复类
模式的精确匹配次数,使用大括号{}表示 ,{n}表示恰好重复n次,{n,}表示至少重复n次,{n,m}表示不少于n次,不多于m次.
/lo{2}k/.test('look') //true /lo{2,5}k/.test('looook') //true
-
量词符
量词符用来设定某个模式出现的次数,是重复类的简写
-
? 表示某个模式出现0次或1次 {0,1}
-
* 表示某个模式出现0次或多次 {0,}
-
+ 表示某个模式出现1次或多次 {1,}
// t 出现0次或1次 /t?est/.test('test') // true /t?est/.test('est') // true // t 出现1次或多次 /t+est/.test('test') // true /t+est/.test('ttest') // true /t+est/.test('est') // false // t 出现0次或多次 /t*est/.test('test') // true /t*est/.test('ttest') // true /t*est/.test('tttest') // true /t*est/.test('est') // true
-
-
贪婪模式
上面三个量词符默认情况下都是最大可能匹配,直到匹配到不能匹配为止,这称为贪婪模式.
如果要将贪婪模式改为非贪婪模式,可以在量词符后加一个?
var s = 'aaa'; s.match(/a+?/) //['a']
一旦条件满足,就不再往下匹配.
除了非贪婪模式的+ 还有非贪婪模式的* 和非贪婪模式的?
- +? 表示某个模式出现1次或者多次,匹配时采用非贪婪模式
- *? 表示某个模式出现0次或多次 ,匹配时采用非贪婪模式
- ?? 表示某个模式出现0次或1次,匹配时采用非贪婪模式
-
组匹配
( ) 表示分组匹配,括号中的模式可以用来匹配分组的内容
/fred+/.test('fredd') //true /(fred)+/.test('fredfred') //true
组匹配非常有用,下面是一个匹配网页标签的例子
var tagName = /<([^>]+)>[^<]*<\/\1>/; tagName.exec("<b>bold</b>")[1] //b
下面是捕获带有属性标签的代码
var html='<b class="hello">Hello</b><i>world</i>'; var tag = /<(\w+)([^>]*)>(.*?)<\/\1>/g; var match = tag.exec(html); match[1] //b match[2] //class=hello match[3] // Hello match = tag.exec(html); match[1] //i match[2] //"" match[3] //world
非捕获组
(?:x) 称为非捕获组,表示不返回该组匹配的内容,匹配结果不计入这个括号
有一个场景 : 需要匹配foo 或者 foofoo,正则写成/(foo){1,2}/
这样写会占用一个组匹配,此时就可以使用非捕获组,将正则改为 /(?:foo){1,2}/ 作用一样,但是不会单独输出括号中的内容
下面是用来分解网址的正则表达式。
// 正常匹配 var url = /(http|ftp):\/\/([^/\r\n]+)(\/[^\r\n]*)?/; url.exec('http://google.com/'); // ["http://google.com/", "http", "google.com", "/"] // 非捕获组匹配 var url = /(?:http|ftp):\/\/([^/\r\n]+)(\/[^\r\n]*)?/; url.exec('http://google.com/'); // ["http://google.com/", "google.com", "/"]
上面的代码中,前一个正则表达式是正常匹配,第一个括号返回网络协议;后一个正则表达式是非捕获匹配,返回结果中不包括网络协议。
(3)先行断言
x(?=y)
称为先行断言(Positive look-ahead),x
只有在y
前面才匹配,y
不会被计入返回结果。比如,要匹配后面跟着百分号的数字,可以写成/\d+(?=%)/
。“先行断言”中,括号里的部分是不会返回的。
var m = 'abc'.match(/b(?=c)/); m // ["b"]
上面的代码使用了先行断言,
b
在c
前面所以被匹配,但是括号对应的c
不会被返回。(4)先行否定断言
x(?!y)
称为先行否定断言(Negative look-ahead),x
只有不在y
前面才匹配,y
不会被计入返回结果。比如,要匹配后面跟的不是百分号的数字,就要写成/\d+(?!%)/
。/\d+(?!\.)/.exec('3.14') // ["14"]
上面代码中,正则表达式指定,只有不在小数点前面的数字才会被匹配,因此返回的结果就是
14
。“先行否定断言”中,括号里的部分是不会返回的。
var m = 'abd'.match(/b(?!c)/); m // ['b']
上面的代码使用了先行否定断言,
b
不在c
前面所以被匹配,而且括号对应的d
不会被返回。