目录
正则表达式
概述
正则表达式(regular expression)是一种表达文本模式(即字符串结构)的方法,有点像字符串的模板,常常用来按照“给定模式”匹配文本。比如,正则表达式给出一个 Email 地址的模式,然后用它来确定一个字符串是否为 Email 地址。
JavaScript 的正则表达式体系是参照 Perl 5 建立的。
新建正则表达式有两种方法。
- 一种是使用字面量,以斜杠表示开始和结束。
var regex = /xyz/;
- 使用RegExp构造函数。
var regex = new RegExp('xyz');
上面两种写法是等价的,都新建了一个内容为xyz的正则表达式对象。
它们的主要区别是,第一种方法在引擎编译代码时,就会新建正则表达式,第二种方法在运行时新建正则表达式,所以前者的效率较高。
而且,前者比较便利和直观,所以实际应用中,基本上都采用字面量定义正则表达式。
实例属性
正则对象的实例属性分成两类。
一类是修饰符相关,返回一个布尔值,表示对应的修饰符是否设置。
RegExp.prototype.ignoreCase:返回一个布尔值,表示是否设置了
i
修饰符。RegExp.prototype.global:返回一个布尔值,表示是否设置了
g
修饰符。(全局搜索)RegExp.prototype.multiline:返回一个布尔值,表示是否设置了
m
修饰符。
上面三个属性都是只读的。
另一类是与修饰符无关的属性,主要是下面两个。
RegExp.prototype.lastIndex:返回一个整数,表示下一次开始搜索的位置。该属性可读写,但是只在进行连续搜索时有意义,详细介绍请看后文。
RegExp.prototype.source:返回正则表达式的字符串形式(不包括反斜杠),该属性只读。
var r = /abc/igm;
r.lastIndex // 0
r.source // "abc"
实例方法
RegExp.prototype.test()
正则实例对象的 test()
返回一个布尔值,表示当前模式是否能匹配参数字符串。
/cat/.test('cats and dogs') // true
上面代码验证参数字符串之中是否包含cat,结果返回true。
如果正则表达式带有 g
修饰符,则每一次 test()
都从上一次结束的位置开始向后匹配。
var r = /x/g;
var s = '_x_x';
r.lastIndex // 0
r.test(s) // true
r.lastIndex // 2
r.test(s) // true
r.lastIndex // 4
r.test(s) // false
RegExp.prototype.exec()
正则实例对象的 exec()
方法,用来返回匹配结果。
如果发现匹配,就返回一个数组,成员是匹配成功的子字符串,否则返回 null。
var s = '_x_x';
var r1 = /x/;
var r2 = /y/;
r1.exec(s) // ["x"]
r2.exec(s) // null
- 组匹配
如果正则表示式包含圆括号(即含有“组匹配”),则返回的数组会包括多个成员。
第一个成员是整个匹配成功的结果,后面的成员就是圆括号对应的匹配成功的组。
也就是说,第二个成员对应第一个括号,第三个成员对应第二个括号,以此类推。
整个数组的length属性等于组匹配的数量再加1。
var s = '_x_x';
var r = /_(x)/;
r.exec(s) // ["_x", "x"]
上面代码的exec方法,返回一个数组。第一个成员是整个匹配的结果,第二个成员是圆括号匹配的结果。
exec方法的返回数组还包含以下两个属性:
input:整个原字符串。
index:整个模式匹配成功的开始位置(从0开始计数)。
字符串的实例方法
字符串的实例方法之中,有4种与正则表达式有关。
String.prototype.match():返回一个数组,成员是所有匹配的子字符串。
String.prototype.search():按照给定的正则表达式进行搜索,返回一个整数,表示匹配开始的位置。
String.prototype.replace():按照给定的正则表达式进行替换,返回替换后的字符串。
String.prototype.split():按照给定规则进行字符串分割,返回一个数组,包含分割后的各个成员。
匹配规则
字面量字符和元字符
大部分字符在正则表达式中,就是字面的含义,比如 /a/
匹配a,/b/
匹配b。
如果在正则表达式之中,某个字符只表示它字面的含义(就像前面的a和b),那么它们就叫做“字面量字符”(literal characters)。
/dog/.test('old dog') // true
- 点字符(
.
)
点字符(.)匹配除回车(\r)、换行(\n) 、行分隔符(\u2028)和段分隔符(\u2029)以外的所有字符。
注意,对于码点大于0xFFFF字符,点字符不能正确匹配,会认为这是两个字符。
/c.t/
上面代码中,c.t匹配c和t之间包含任意一个字符的情况,只要这三个字符在同一行,比如cat、c2t、c-t等等,但是不匹配coot。
- 位置字符
位置字符用来提示字符所处的位置,主要有两个字符。
^
表示字符串的开始位置
$
表示字符串的结束位置
// test必须出现在开始位置
/^test/.test('test123') // true
// test必须出现在结束位置
/test$/.test('new test') // true
// 从开始位置到结束位置只有test
/^test$/.test('test') // true
/^test$/.test('test test') // false
- 选择符(
|
)
竖线符号(|)在正则表达式中表示“或关系”(OR),即 cat|dog
表示匹配cat或dog。
/11|22/.test('911') // true
上面代码中,正则表达式指定必须匹配11或22。
转义符
正则表达式中那些有特殊含义的元字符,如果要匹配它们本身,就需要在它们前面要加上反斜杠。比如要匹配+,就要写成\+
。
/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
- 脱字符(
^
)
如果方括号内的第一个字符是[^],则表示除了字符类之中的字符,其他字符都可以匹配。比如,[^xyz]表示除了x、y、z之外都可以匹配。
/[^abc]/.test('hello world') // true
/[^abc]/.test('bbc') // false
注意,脱字符只有在字符类的第一个位置才有特殊含义,否则就是字面含义。
- 连字符(
-
)
某些情况下,对于连续序列的字符,连字符(-)用来提供简写形式,表示字符的连续范围。
比如,[abc]可以写成[a-c],[0123456789]可以写成[0-9],同理[A-Z]表示26个大写字母。
/a-z/.test('b') // false
/[a-z]/.test('b') // true
预定义模式
预定义模式指的是某些常见模式的简写方式。
\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
匹配非词边界,即在词的内部。
// \s 的例子
/\s\w*/.exec('hello world') // [" world"]
// \b 的例子
/\bworld/.test('hello world') // true
/\bworld/.test('hello-world') // true
/\bworld/.test('helloworld') // false
// \B 的例子
/\Bworld/.test('hello-world') // false
/\Bworld/.test('helloworld') // true
重复类
模式的精确匹配次数,使用大括号({}
)表示。{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+/) // ["aaa"]
除了非贪婪模式的加号,还有非贪婪模式的星号(*)。
*?
:表示某个模式出现0次或多次,匹配时采用非贪婪模式。+?
:表示某个模式出现1次或多次,匹配时采用非贪婪模式。
修饰符
修饰符(modifier)表示模式的附加规则,放在正则模式的最尾部。
修饰符可以单个使用,也可以多个一起使用。
// 单个修饰符
var regex = /test/i;
// 多个修饰符
var regex = /test/ig;
- g 修饰符
默认情况下,第一次匹配成功后,正则对象就停止向下匹配了。
g 修饰符表示全局匹配(global),加上它以后,正则对象将匹配全部符合条件的结果,主要用于搜索和替换。
var regex = /b/;
var str = 'abba';
regex.test(str); // true
regex.test(str); // true
regex.test(str); // true
- i 修饰符
默认情况下,正则对象区分字母的大小写,加上i修饰符以后表示忽略大小写(ignorecase)。
/abc/.test('ABC') // false
/abc/i.test('ABC') // true
- m 修饰符
m修饰符表示多行模式(multiline),会修改^和 匹配字符串的开始处和结尾处,加上m修饰符以后,^和 会识别换行符(\n)。
/world$/.test('hello world\n') // false
/world$/m.test('hello world\n') // true
组匹配
概述
正则表达式的括号表示分组匹配,括号中的模式可以用来匹配分组的内容。
/fred+/.test('fredd') // true
/(fred)+/.test('fredfred') // true
非捕获组
(?:x)
称为非捕获组(Non-capturing group),表示不返回该组匹配的内容,即匹配的结果中不计入这个括号。
非捕获组的作用请考虑这样一个场景,假定需要匹配foo或者foofoo,正则表达式就应该写成/(foo){1, 2}/,但是这样会占用一个组匹配。
这时,就可以使用非捕获组,将正则表达式改为/(?:foo){1, 2}/,它的作用与前一个正则是一样的,但是不会单独输出括号内部的内容。
请看下面的例子。
var m = 'abc'.match(/(?:.)b(.)/);
m // ["abc", "c"]
上面代码中的模式,一共使用了两个括号。其中第一个括号是非捕获组,所以最后返回的结果中没有第一个括号,只有第二个括号匹配的内容。
先行断言
x(?=y)
称为先行断言(Positive look-ahead),x只有在y前面才匹配,y不会被计入返回结果。
比如,要匹配后面跟着百分号的数字,可以写成/\d+(?=%)/。
“先行断言”中,括号里的部分是不会返回的。
var m = 'abc'.match(/b(?=c)/);
m // ["b"]
上面的代码使用了先行断言,b在c前面所以被匹配,但是括号对应的c不会被返回。
先行否定断言
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不会被返回。
原文地址
本教程采用知识共享 署名-相同方式共享 3.0协议