正则 概述 是一种表达式模板,用于匹配文本
创建正则实例的两种方法
6 1 2 var regex = /xyz/; var regex = new RegExp('xyz');
实例方法 test,exec 正则实例对象的test方法返回一个布尔值,表示当前模式是否能匹配参数字符串。
6 1 /cat/.test('cats and dogs') // true
正则实例对象的exec()方法,用来返回匹配结果。如果发现匹配,就返回一个数组,成员是匹配成功的子字符串,否则返回null。
6 1 2 3 4 5 6 var s = '_x_x'; var r1 = /x/; var r2 = /y/; r1.exec(s) // ["x"] r2.exec(s) // null
字符串实例方法 match,replace,split 字符串的实例方法之中,有4种与正则表达式有关。
String.prototype.match():返回一个数组,成员是所有匹配的子字符串。
String.prototype.search():按照给定的正则表达式进行搜索,返回一个整数,表示匹配开始的位置。
String.prototype.replace():按照给定的正则表达式进行替换,返回替换后的字符串。
String.prototype.split():按照给定规则进行字符串分割,返回一个数组,包含分割后的各个成员。
匹配规则 元字符(点字符,位置字符,选择字符) 点字符 点字符(.)匹配除回车(\r)、换行(\n) 、行分隔符(\u2028)和段分隔符(\u2029)以外的所有字符。
注意,对于码点大于0xFFFF字符,点字符不能正确匹配,会认为这是两个字符。
6 1 2 /c.t/ // 可以匹配 c 和 t 中间任意单个字符的情况 如 cat c-t, 但不匹配coot
位置字符 位置字符用来提示字符所处的位置,主要有两个字符。
^表示必须出现在开头
$表示必须出现在结尾
6 1 2 3 4 5 /^test/.test('test123'); // true // 从开始位置到结束位置只有test /^test$/.test('test'); // true /^test$/.test('test test'); // false
选择符 表示或关系,即cat|dog表示匹配cat或dog。
6 1 /11|22/.test('911') // true
字符类( [ ] ) 字符类(class)表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内,
比如[xyz] 表示x、y、z之中任选一个匹配。
6 1 2 /[abc]/.test('hello world'); // false /[abc]/.test('apple'); // true
脱字符 如果方括号内的第一个字符是[^],则表示除了字符类之中的字符,其他字符都可以匹配。
比如,[^xyz]表示除了x、y、z之外都可以匹配。
6 1 2 /[^abc]/.test('bbc news'); // true /[^abc]/.test('bbc'); // false
连字符 某些情况下,对于连续序列的字符,连字符(-)用来提供简写形式,表示字符的连续范围。
比如,[abc]可以写成[a-c],[0123456789]可以写成[0-9],同理[A-Z]表示26个大写字母。
6 1 2 /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 匹配非词边界,即在词的内部。
6 1 2 3 4 5 var html = "<b>Hello</b>\n<i>world!</i>"; /[\S\s]*/.exec(html)[0] // "<b>Hello</b>\n<i>world!</i>" //上面代码中,[\S\s]指代一切字符。
重复类 模式的精确匹配次数,使用大括号({})表示。{n}表示恰好重复n次,{n,}表示至少重复n次,
{n,m}表示重复不少于n次,不多于m次。
6 1 2 /lo{2}k/.test('look'); // true /lo{2,5}k/.test('looook') // true
量词符 量词符用来设定某个模式出现的次数。
? 问号表示某个模式出现0次或1次,等同于{0, 1}。
* 星号表示某个模式出现0次或多次,等同于{0,}。
+ 加号表示某个模式出现1次或多次,等同于{1,}。
这三个量词符默认是贪婪模式,即匹配到下一个字符不满足为止
6 1 2 var s = 'aaa'; s.match(/a+/) // ["aaa"]
在量词符后加上 ? 可以改为非贪婪模式
6 1 2 var s = 'aaa'; s.match(/a+?/) // ["a"]
修饰符 (i,g,m,s,u) i,g,m,可同时使用
g修饰符 一般第一次匹配成功后,正则对象就停止向下匹配了。g修饰符表示全局匹配(global),
加上它以后,正则对象将匹配全部符合条件的结果,主要用于搜索和替换。
6 1 2 3 4 'a bird lying in the bed'.match(/b\w*/); // 返回 ['bird', index: 2, input: 'a bird lying in the bed', groups: undefined] 'a bird lying in the bed'.match(/b\w*/g) // ['bird', 'bed']
此处的\w代表匹配任意的字母、数字和下划线, *代表这个\w出现0次或多次
i修饰符 默认情况下,正则对象区分字母的大小写,加上i修饰符以后表示忽略大小写
6 1 2 /abc/.test('ABC'); // false /abc/i.test('ABC') // true
m修饰符 m修饰符表示多行模式(multiline),会修改^和$的行为。默认情况下(即不加m修饰符时),^和$匹配字符串的
开始处和结尾处,加上m修饰符以后,^和$还会匹配行首和行尾,即^和$会识别换行符(\n)。
6 1 2 /world$/.test('hello world\n');// false /world$/m.test('hello world\n') // true
上面的代码中,字符串结尾处有一个换行符。如果不加m修饰符,匹配不成功,因为字符串的结尾不是world;
加上以后,$可以匹配行尾。
s修饰符 正则表达式中,点(.)是一个特殊字符,代表任意的单个字符,但是有两个例外。一个是四个字节的 UTF-16 字符,
这个可以用u修饰符解决;另一个是行终止符(line terminator character)。主要就是 换行\n以及回车\r,
但有时候我们就是想能够匹配任意字符,es5中有一种变通的方法,使用 [^] 代替. ,
在ES2018中引入了s修饰符,它使得.可以匹配任意单个字符
6 1 2 3 4 5 /foo.bar/.test('foo\nbar'); // false /foo[^]bar/.test('foo\nbar'); // true /foo.bar/s.test('foo\nbar'); // true
组匹配 正则表达式的括号表示分组匹配,括号中的模式可以用来匹配分组的内容。
6 1 2 /fred+/.test('fredd'); // true /(fred)+/.test('fredfred') // true
上面代码中,第一个模式没有括号,结果+只表示重复字母d,第二个模式有括号,结果+就表示匹配fred这个词。
6 1 2 3 var m = 'abcabc'.match(/(.)b(.)/); m // ['abc', 'a', 'c']
可以看到 组匹配的内容 a, c 也被输出了出来
正则表达式内部,还可以用\n引用括号匹配的内容,n是从1开始的自然数,表示对应顺序的括号。
6 1 /y(..)(.)\2\1/.test('yabccab') // true
组匹配非常有用,下面是一个匹配网页标签的例子。
6 1 2 3 4 var tagName = /<([^>]+)>[^<]*<\/\1>/; tagName.exec("<b>bold</b>")[1] // 'b'
匹配时间的例子
6 1 2 3 4 5 6 const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/; const matchObj = RE_DATE.exec('1999-12-31'); const year = matchObj[1]; // 1999 const month = matchObj[2]; // 12 const day = matchObj[3]; // 31
组匹配的一个问题是,每一组的匹配含义不容易看出来,而且只能用数字序号(比如matchObj[1])引用,要是组的顺序变了,引用的时候就必须修改序号。
ES2018 引入了具名组匹配(Named Capture Groups),允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。
6 1 2 3 4 5 6 const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; const matchObj = RE_DATE.exec('1999-12-31'); const year = matchObj.groups.year; // "1999" const month = matchObj.groups.month; // "12" const day = matchObj.groups.day; // "31"
上面代码中,“具名组匹配”在圆括号内部,模式的头部添加“问号 + 尖括号 + 组名”(?<year>),然后就可以在exec方法返回结果的groups属性上引用该组名。同时,数字序号(matchObj[1])依然有效。
引用具名组
6 1 2 3 4 let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u; '2015-01-02'.replace(re, '$<day>/$<month>/$<year>') // '02/01/2015'
正则内部引用具名匹配组,使用 \k<组名>的写法
6 1 2 3 const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/; RE_TWICE.test('abc!abc') // true RE_TWICE.test('abc!ab') // false