深入浅出JS正则表达式

正则表达式是个什么东东

我们在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。

很可能你使用过Windows/Dos下用于文件查找的通配符(wildcard),也就是 *?

如果你想查找某个目录下的所有的Word文档的话,你会搜索 *.doc。在这里,*会被解释成任意的字符串。

和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需求——当然,代价就是更复杂——比如你可以编写一个正则表达式,用来查找所有以 0 开头,后面跟着 2-3 个数字,然后是一个连字号“-”,最后是7或8位数字的字符串(像010-12345678或0376-7654321)。

正则表达式是用于匹配字符串中字符组合的模式。

在 JavaScript中,正则表达式也是对象。

利用图形化工具理解正则表达式

​推荐一个辅助理解正则表达式的在线工具➡️ https://regexper.com/

语法没懂表着急,后面会有,这里只是学会用工具帮助我们学习。

  • 手机号正则/^1[34578][0-9]{9}$/

phoneExp

注释: 以1开头,第二位为3 4 5 7 9 其中一个,以9位(本身1次加重复8次)0-9数字结尾

  • 单词边界/\bis\b/

单词边界

注释: is前后都是单词的边界,比较晦涩难懂?感受下两者的区别,\b 会放到语法部分讲解。

单词边界Console

  • URL分组替换/http:(\/\/.+\.jpg)/

URLGroupReplace

正则表达式中括号用来分组,这个时候我们可以通过用$1来获取 group#1的内容

URLGroupConsole

说下这个正则的意义,如果网站用了https,网站引用静态资源也必须是https,否则报错。如果写成 // 会自动识别 http 或者 https

  • 日期匹配与分组替换/^\d{4}[/-]\d{2}[/-]\d{2}$/

DateReplaceExp

这个正则比较复杂,画符念咒的地方太多了,一一分析:

  1. Start of line 是由^生效的表示以此开头
  2. 对应结尾End of line 由$生效表示以此结尾
  3. 接着看digit 由 \d 生效表示数字
  4. 3times 由{4} 生效表示重复4次,开始的时候有疑问,为什么不是 4times 。后来明白作者的用意,正则表达式是一个规则,用这个规则去从字符串开始匹配到结束(注意计算机读字符串可是不会分行的,都是一个串,我们看到的多行,人家会认为是个 \t )这里设计好像小火车的轨道一直开到末尾。digit 传过一次,3times表示再来三次循环,共4次,后面的once同理。
  5. 接下来,是 one of 在手机正则里面已经出现了。表示其中的任意一个。

这个正则解释完了,接下来用它做什么呢?我们可以验证日期的合法性。

DateExpConsole

结合URL分组替换所用到的分组特性,我们可以轻松写出日期格式化的方法

改造下这个正则:/^(\d{4})[/-](\d{2})[/-](\d{2})$/

DateGroupExp

轻松的可以拿到 group#1 #2 #3 的内容,对应 $1 $2 $3

DateGroupConsole

到现在已经能结合图形化工具看懂正则表达式了,如果想自己写,还是要掌握正则式语法。

正则表达式语法

RegExp 对象

Javascript 通过内置对象RegExp支持正则表达式,它是对字符串执行模式匹配的强大工具。

有两种方法来创建一个RegExp对象:

  • 直接量(字面量) 语法:/pattern/flags
  • RegExp 构造函数 语法:new RegExp(pattern [,flags])

参数说明:

pattern :正则表达式的文本。

flags 可选。如果指定,标志可以具有以下值的任意组合:

  • g :全局匹配;找到所有匹配,而不是在第一个匹配后停止
  • i :忽略大小写
  • m :多行; 将开始和结束字符(^和$)视为在多行上工作(例如,分别匹配每一行的开始和结束(由 \n 或 \r 分割),而不只是只匹配整个输入字符串的最开始和最末尾处。
  • u :Unicode; 将模式视为Unicode序列点的序列
  • y :粘性匹配; 仅匹配目标字符串中此正则表达式的lastIndex属性指示的索引(并且不尝试从任何后续的索引匹配)。

下面示例如何新建正则表达式:

1
2
3
4
//使用字面量,以斜杠/表示开始和结束。
var regex = /xyz/;
//使用 RegExp 构造函数
var regex = new RegExp('xyz');

上面两种写法是等价的,都新建了一个内容为xyz的正则表达式对象。它们的主要区别是,第一种方法在编译时新建正则表达式,第二种方法在运行时新建正则表达式。

1
2
3
var regex = new RegExp('xyz', "i");
// 等价于
var regex = /xyz/i;

上面代码中,正则表达式/xyz/有一个修饰符i

字面量和构造函数——在运行时有一个细微的区别:

  • 采用字面量的写法,正则对象在代码载入时(即编译时)生成;
  • 采用构造函数的方法,正则对象在代码运行时生成。

考虑到书写的便利和直观,实际应用中,基本上都采用字面量的写法。

正则对象生成以后,有两种使用方式:

  • 正则对象的方法:将字符串作为参数,比如regex.test(string)
  • 字符串对象的方法:将正则对象作为参数,比如string.match(regex)

正则对象的属性和方法

属性

正则对象的属性分成两类。

一类是修饰符相关,返回一个布尔值,表示对应的修饰符是否设置。

  • ignoreCase:返回一个布尔值,表示是否设置了i修饰符,该属性只读。
  • global:返回一个布尔值,表示是否设置了g修饰符,该属性只读。
  • multiline:返回一个布尔值,表示是否设置了m修饰符,该属性只读。
1
2
3
4
var r = /abc/igm;
r.ignoreCase // true
r.global // true
r.multiline // true

另一类是与修饰符无关的属性,主要是下面两个:

  • lastIndex :它声明的是上一次匹配文本之后的第一个字符的位置。返回下一次开始搜索的位置。该属性可读写,但是只在设置了g修饰符时有意义。
  • source :返回正则表达式的字符串形式(不包括斜杠/),该属性只读。
1
2
3
var r = /abc/igm;
r.lastIndex // 0
r.source // "abc"

方法

test()

正则对象的test方法返回一个布尔值,表示当前模式是否能匹配参数字符串。

1
2
//验证参数字符串之中是否包含cat,结果返回true。
/cat/.test('cats and dogs') // true

如果正则表达式带有g修饰符,则每一次test方法都从上一次结束的位置开始向后匹配。

1
2
3
4
5
6
7
8
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

上面代码的正则对象使用了g修饰符,表示要记录搜索位置。接着,三次使用test方法,每一次开始搜索的位置都是上一次匹配的后一个位置。

带有g修饰符时,可以通过正则对象的lastIndex属性指定开始搜索的位置。

1
2
3
4
var r = /x/g;
var s = '_x_x';
r.lastIndex = 4;
r.test(s) // false

上面代码指定从字符串的第五个位置开始搜索,这个位置是没有字符的,所以返回false

lastIndex属性只对同一个正则表达式有效,所以下面这样写是错误的。

1
2
var count = 0;
while (/a/g.test('babaa')) count++;

上面代码会导致无限循环,因为while循环的每次匹配条件都是一个新的正则表达式,导致lastIndex属性总是等于0。

如果正则模式是一个空字符串,则匹配所有字符串。

1
new RegExp('').test('abc');// true
exec()

正则对象的exec方法,可以返回匹配结果。如果发现匹配,就返回一个数组,成员是每一个匹配成功的子字符串,否则返回null

1
2
3
4
5
var s = '_x_x';
var r1 = /x/;
var r2 = /y/;
r1.exec(s) // ["x"]
r2.exec(s) // null

上面代码中,正则对象r1匹配成功,返回一个数组,成员是匹配结果;正则对象r2匹配失败,返回null

如果正则表示式包含圆括号(即含有“组匹配”),则返回的数组会包括多个成员。第一个成员(组号为0)是整个匹配成功的结果,后面的成员就是圆括号对应的匹配成功的组。也就是说,第二个成员(组号为1)对应第一个括号,第三个成员(组号为2)对应第二个括号,以此类推。整个数组的length属性等于组匹配的数量再加1。

1
2
3
var s = '_x_x';
var r = /_(x)/;
r.exec(s) // ["_x", "x"]

上面代码的exec方法,返回一个数组。第一个成员是整个匹配的结果,第二个成员是圆括号匹配的结果

exec方法的返回数组还包含以下两个属性:

  • input:整个原字符串。
  • index:整个模式匹配成功的开始位置(从0开始计数)。
1
2
3
4
5
var r = /a(b+)a/;
var arr = r.exec('_abbba_aba_');
arr // ["abbba", "bbb"]
arr.index // 1
arr.input // "_abbba_aba_"

上面代码中的index属性等于1,是因为从原字符串的第二个位置开始匹配成功。

如果正则表达式加上g修饰符,则可以使用多次exec方法,下一次搜索的位置从上一次匹配成功结束的位置开始。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var r = /a(b+)a/g;
var a1 = r.exec('_abbba_aba_');
a1 // ['abbba', 'bbb']
a1.index // 1
r.lastIndex // 6
var a2 = r.exec('_abbba_aba_');
a2 // ['aba', 'b']
a2.index // 7
r.lastIndex // 10
var a3 = r.exec('_abbba_aba_');
a3 // null
a3.index // TypeError: Cannot read property 'index' of null
r.lastIndex // 0
var a4 = r.exec('_abbba_aba_');
a4 // ['abbba', 'bbb']
a4.index // 1
r.lastIndex // 6

上面代码连续用了四次exec方法,前三次都是从上一次匹配结束的位置向后匹配。当第三次匹配结束以后,整个字符串已经到达尾部,正则对象的lastIndex属性重置为0,意味着第四次匹配将从头开始。

利用g修饰符允许多次匹配的特点,可以用一个循环完成全部匹配。

1
2
3
4
5
6
7
8
9
var r = /a(b+)a/g;
var s = '_abbba_aba_';
while(true) {
var match = r.exec(s);//第一次执行该方法,原字符匹配成功位置在‘_abbba’最后一个a上。lastIndex为6,即a后面的‘_’字符,从这个位置开始。(下一次搜索的位置从上一次匹配成功结束的位置开始。)
if (!match) break;//循环到第三次匹配时,lastIndex为10,即到s的最后一个字符“_”,match为null,!match为false,循环break。
console.log(match[1]);
}
// bbb
// b

正则对象的lastIndex属性不仅可读,还可写。一旦手动设置了lastIndex的值,就会从指定位置开始匹配。但是,这只在设置了g修饰符的情况下,才会有效。

1
2
3
4
5
var r = /a/;
r.lastIndex = 7; // 无效
var match = r.exec('xaxa');
match.index // 1
r.lastIndex // 7

上面代码设置了lastIndex属性,但是因为正则表达式没有g修饰符,所以是无效的。每次匹配都是从字符串的头部开始。

如果有g修饰符,lastIndex属性就会生效。

1
2
3
4
5
var r = /a/g;
r.lastIndex = 2;
var match = r.exec('xaxa');
match.index // 3
r.lastIndex // 4

上面代码中,lastIndex属性指定从字符的第三个位置开始匹配。成功后,下一次匹配就是从第五个位置开始。

如果正则对象是一个空字符串,则exec方法会匹配成功,但返回的也是空字符串。

1
2
3
4
5
6
7
8
9
10
var r1 = new RegExp('');
var a1 = r1.exec('abc');
a1 // ['']
a1.index // 0
r1.lastIndex // 0
var r2 = new RegExp('()');
var a2 = r2.exec('abc');
a2 // ['', '']--第一个是整个匹配的结果,第二个是圆括号匹配的结果。
a2.index // 0
r2.lastIndex // 0

字符串对象的方法

字符串对象的方法之中,有4种与正则对象有关。

  • match():返回一个数组,成员是所有匹配的子字符串。
  • search():按照给定的正则表达式进行搜索,返回一个整数,表示匹配开始的位置。
  • replace():按照给定的正则表达式进行替换,返回替换后的字符串。
  • split():按照给定规则进行字符串分割,返回一个数组,包含分割后的各个成员。

方法

String.prototype.match()

字符串对象的match方法对字符串进行正则匹配,返回匹配结果。

该方法用于在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。该方法类似于indexOf()或者lastIndexOf() ,但是它返回的是指定的值,而不是字符串的位置。

基础语法:

stringObject.match(searchValue) 或者 stringObject.match(regexp)

@param(参数)

searchValue 需要检索字符串的值;

​ regexp: 需要匹配模式的RegExp对象;

@return(返回值)

存放匹配成功的数组; 它可以全局匹配模式,全局匹配的话,它返回的是一个数组。如果没有找到任何的一个匹配,那么它将返回的是null;返回的数组内有三个元素,第一个元素的存放的是匹配的文本,还有二个对象属性;index属性表明的是匹配文本的起始字符在stringObject中的位置;input属性声明的是对stringObject对象的引用;

测试代码:

1
2
3
4
5
6
7
var str = "hello world";
console.log(str.match("hello")); // ["hello", index: 0, input: "hello world"]
console.log(str.match("Hello")); // null
console.log(str.match(/hello/)); // ["hello", index: 0, input: "hello world"]
// 全局匹配
var str2="1 plus 2 equal 3"
console.log(str2.match(/\d+/g)); //["1", "2", "3"]
1
2
3
4
5
var s = '_x_x';
var r1 = /x/;
var r2 = /y/;
s.match(r1) // ["x"]
s.match(r2) // null

从上面代码可以看到,字符串的match方法与正则对象的exec方法非常类似:匹配成功返回一个数组,匹配失败返回null

如果正则表达式带有g修饰符,则该方法与正则对象的exec方法行为不同,会一次性返回所有匹配成功的结果。

1
2
3
4
var s = 'abba';
var r = /a/g;
s.match(r) // ["a", "a"]
r.exec(s) // ["a"]

设置正则表达式的lastIndex属性,对match方法无效,匹配总是从字符串的第一个字符开始

1
2
3
4
var r = /a|b/g;
r.lastIndex = 7;
'xaxb'.match(r) // ['a', 'b']
r.lastIndex // 0

字符串对象的search方法,返回第一个满足条件的匹配结果在整个字符串中的位置。如果没有任何匹配,则返回-1

基本语法:

stringObject.search(regexp);

@param

参数regexp可以需要在stringObject中检索的字符串,也可以 是需要检索的RegExp对象。

@return(返回值)

stringObject中第一个与regexp对象相匹配的子串的起始位置。如果没有找到任何匹配的子串,则返回-1;

注意: search()方法不执行全局匹配,它将忽略标志g,同时它也没有regexp对象的lastIndex的属性,且总是从字符串开始位置进行查找,总是返回的是 stringObject 匹配的第一个位置。

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
var str = "hello world,hello world";
// 返回匹配到的第一个位置(使用的regexp对象检索)
console.log(str.search(/hello/)); // 0
// 没有全局的概念 总是返回匹配到的第一个位置
console.log(str.search(/hello/g)); //0
console.log(str.search(/world/)); // 6
// 也可以是检索字符串中的字符
console.log(str.search("wo")); // 6
// 如果没有检索到的话,则返回-1
console.log(str.search(/longen/)); // -1
// 我们检索的时候 可以忽略大小写来检索
var str2 = "Hello";
console.log(str2.search(/hello/i)); // 0
1
'_x_x'.search(/x/);// 1

上面代码中,第一个匹配结果出现在字符串的1号位置。

该方法会忽略g修饰符。

1
2
3
var r = /x/g;
r.lastIndex = 2; // 无效
'_x_x'.search(r) // 1

上面代码中,正则表达式使用g修饰符之后,使用lastIndex属性指定开始匹配的位置,结果无效,还是从字符串的第一个字符开始匹配。

String.prototype.replace()

字符串对象的replace方法可以替换匹配的值。它接受两个参数,第一个是搜索模式,第二个是替换的内容。

基本语法:

stringObject.replace(regexp/substr,replacement);

@param(参数)

​ regexp/substr; 字符串或者需要替换模式的RegExp对象。

​ replacement:一个字符串的值,被替换的文本或者生成替换文本的函数。

@return(返回值) 返回替换后的新字符串

注意: 字符串的stringObject的replace()方法执行的是查找和替换操作,替换的模式有2种,既可以是字符串,也可以是正则匹配模式,如果是正则匹配模式的话,那么它可以加修饰符g,代表全局替换,否则的话,它只替换第一个匹配的字符串;

replacement 既可以是字符串,也可以是函数,如果它是字符串的话,那么匹配的将与字符串替换,replacement中的$有具体的含义,如下:

  • $1 ,$2 ,$3 …. $99含义是:与regexp中的第1到第99个子表达式相匹配的文本。
  • $& 的含义是:与RegExp相匹配的子字符串。
  • lastMatchRegExp[“$_”] 的含义是:返回任何正则表达式搜索过程中的最后匹配的字符。
  • lastParenRegExp[“$+”]的含义是:返回任何正则表达式查找过程中最后括号的子匹配。
  • leftContextRegExp[“$`”]的含义是:返回被查找的字符串从字符串开始的位置到最后匹配之前的位置之间的字符。
  • rightContextRegExp[“$'”]的含义是:返回被搜索的字符串中从最后一个匹配位置开始到字符串结尾之间的字符。

搜索模式如果不加g修饰符,就替换第一个匹配成功的值,否则替换所有匹配成功的值。

1
2
3
'aaa'.replace('a', 'b') // "baa"
'aaa'.replace(/a/, 'b') // "baa"
'aaa'.replace(/a/g, 'b') // "bbb"

上面代码中,最后一个正则表达式使用了g修饰符,导致所有的b都被替换掉了。

replace方法的一个应用,就是消除字符串首尾两端的空格。

1
2
3
var str = ' #id div.class ';
str.replace(/^\s+|\s+$/g, '')
// "#id div.class"

replace方法的第二个参数可以使用美元符号$,用来指代所替换的内容。

  • $& 指代匹配的子字符串。
  • $` 指代匹配结果前面的文本。
  • $' 指代匹配结果后面的文本。
  • $n 指代匹配成功的第n组内容,n是从1开始的自然数。
  • $$ 指代美元符号$

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
var str = "hello world";
// 替换字符串
var s1 = str.replace("hello","a");
console.log(s1);// a world
// 使用正则替换字符串
var s2 = str.replace(/hello/,"b");
console.log(s2); // b world
// 使用正则全局替换 字符串
var s3 = str.replace(/l/g,'');
console.log(s3); // heo word
// $1,$2 代表的是第一个和第二个子表达式相匹配的文本
// 子表达式需要使用小括号括起来,代表的含义是分组
var name = "longen,yunxi";
var s4 = name.replace(/(\w+)\s*,\s*(\w+)/,"$2 $1");
console.log(s4); // "yunxi,longen"
// $& 是与RegExp相匹配的子字符串
var name = "hello I am a chinese people";
var regexp = /am/g;
if(regexp.test(name)) {
//返回正则表达式匹配项的字符串
console.log(RegExp['$&']); // am
//返回被搜索的字符串中从最后一个匹配位置开始到字符串结尾之间的字符。
console.log(RegExp["$'"]); // a chinese people
//返回被查找的字符串从字符串开始的位置到最后匹配之前的位置之间的字符。
console.log(RegExp['$`']); // hello I
// 返回任何正则表达式查找过程中最后括号的子匹配。
console.log(RegExp['$+']); // 空字符串
//返回任何正则表达式搜索过程中的最后匹配的字符。
console.log(RegExp['$_']); // hello I am a chinese people
}
// replace 第二个参数也可以是一个function 函数
var name2 = "123sdasadsr44565dffghg987gff33234";
name2.replace(/\d+/g,function(v){
console.log(v);
/*
* 第一次打印123
* 第二次打印44565
* 第三次打印987
* 第四次打印 33234
*/
});
/*
* 如下函数,回调函数参数一共有四个
* 第一个参数的含义是 匹配的字符串
* 第二个参数的含义是 正则表达式分组内容,没有分组的话,就没有该参数,
* 如果没有该参数的话那么第四个参数就是undefined
* 第三个参数的含义是 匹配项在字符串中的索引index
* 第四个参数的含义是 原字符串
*/
name2.replace(/(\d+)/g,function(a,b,c,d){
console.log(a);
console.log(b);
console.log(c);
console.log(d);
/*
* 如上会执行四次,值分别如下(正则使用小括号,代表分组):
* 第一次: 123,123,0,123sdasadsr44565dffghg987gff33234
* 第二次: 44565,44565,11,123sdasadsr44565dffghg987gff33234
* 第三次: 987,987,22,123sdasadsr44565dffghg987gff33234
* 第四次: 33234,33234,28,123sdasadsr44565dffghg987gff33234
*/
});
1
2
'hello world'.replace(/(\w+)\s(\w+)/, '$2 $1'); // "world hello"
'abc'.replace('b', '[$`-$&-$\']'); // "a[a-b-c]c"

replace方法的第二个参数还可以是一个函数,将每一个匹配内容替换为函数返回值。

1
2
3
4
5
6
7
8
'3 and 5'.replace(/[0-9]+/g, function(match){
return 2 * match;
}); // "6 and 10"
var a = 'The quick brown fox jumped over the lazy dog.';
var pattern = /quick|brown|lazy/ig;
a.replace(pattern, function replacer(match) {
return match.toUpperCase();
});// The QUICK BROWN fox jumped over the LAZY dog.

作为replace方法第二个参数的替换函数,可以接受多个参数。第一个参数是捕捉到的内容,第二个参数是捕捉到的组匹配(有多少个组匹配,就有多少个对应的参数)。此外,最后还可以添加两个参数,倒数第二个参数是捕捉到的内容在整个字符串中的位置(比如从第五个位置开始),最后一个参数是原字符串。下面是一个网页模板替换的例子。

1
2
3
4
5
6
7
8
9
10
11
12
var prices = {
'pr_1': '$1.99',
'pr_2': '$9.99',
'pr_3': '$5.00'
};
var template = '/* ... */'; // 这里可以放网页模块字符串
template.replace(
/(<span id=")(.*?)(">)(<\/span>)/g,
function(match, $1, $2, $3, $4){
return $1 + $2 + $3 + prices[$2] + $4;
}
);

上面代码的捕捉模式中,有四个括号,所以会产生四个组匹配,在匹配函数中用$1$4表示。匹配函数的作用是将价格插入模板中。

String.prototype.split()

字符串对象的split方法按照正则规则分割字符串,返回一个由分割后的各个部分组成的数组。

基本语法:

stringObject.split(separator,howmany);

**@param(参数) **

  1. separator[必填项]:字符串或正则表达式,该参数指定的地方分割stringObject;
  2. howmany[可选]:该参数指定返回的数组的最大长度,如果设置了该参数,返回的子字符串不会多于这个参数指定的数组。如果没有设置该参数的话,整个字符串都会被分割,不考虑他的长度。

@return(返回值) 一个字符串数组。该数组通过在separator指定的边界处将字符串stringObject 分割成子字符串。

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var str = "what are you doing?";
// 以" "分割字符串
console.log(str.split(" "));
// 打印 ["what", "are", "you", "doing?"]
// 以 "" 分割字符串
console.log(str.split(""));
/*
* 打印:["w", "h", "a", "t", " ", "a", "r", "e", " ", "y", "o", "u", " ", "d", "o", "i", "n",
* "g", "?"]
*/
// 指定返回数组的最大长度为3
console.log(str.split("",3));
// 打印 ["w", "h", "a"]

该方法接受两个参数,第一个参数是分隔规则,第二个参数是返回数组的最大成员数。

1
2
3
4
5
6
// 非正则分隔,以“,”分割
'a, b,c, d'.split(','); //[ 'a', ' b', 'c', ' d' ]
// 正则分隔,去除多余的空格
'a, b,c, d'.split(/, */); //[ 'a', 'b', 'c', 'd' ]
// 指定返回数组的最大成员
'a, b,c, d'.split(/, */, 2); //[ 'a', 'b' ]

上面代码使用正则表达式,去除了子字符串的逗号后面的空格。

1
2
3
4
// 例一
'aaa*a*'.split(/a*/); // [ '', '*', '*' ]
// 例二
'aaa**a*'.split(/a*/); // ["", "*", "*", "*"]

上面代码的分割规则是0次或多次的a,由于正则默认是贪婪匹配,所以例一的第一个分隔符是aaa,第二个分割符是a,将字符串分成三个部分,包含开始处的空字符串。例二的第一个分隔符是aaa,第二个分隔符是0个a(即空字符),第三个分隔符是a,所以将字符串分成四个部分。

如果正则表达式带有括号,则括号匹配的部分也会作为数组成员返回。

1
'aaa*a*'.split(/(a*)/); // [ '', 'aaa', '*', 'a', '*' ]

上面代码的正则表达式使用了括号,第一个组匹配是“aaa”,第二个组匹配是“a”,它们都作为数组成员返回。

匹配规则

字面量字符(原义字符)

大部分字符在正则表达式中,就是字面的含义,比如/a/匹配a/b/匹配b。如果在正则表达式之中,某个字符只表示它字面的含义(就像前面的ab),那么它们就叫做“字面量字符”(literal characters)。

1
/dog/.test("old dog") // true

上面代码中正则表达式的dog,就是字面量字符,所以/dog/匹配“old dog”,因为它就表示“d”、“o”、“g”三个字母连在一起。

除了字面量字符以外,还有一部分字符有特殊含义,不代表字面的意思。它们叫做“元字符”(metacharacters)

方括号

方括号用于查找某个范围内的字符:

表达式 描述
[abc] 查找方括号之间的任何字符。
[^abc] 查找任何不在方括号之间的字符。
[0-9] 查找任何从 0 至 9 的数字。
[a-z] 查找任何从小写 a 到小写 z 的字符。
[A-Z] 查找任何从大写 A 到大写 Z 的字符。
[A-z] 查找任何从大写 A 到小写 z 的字符。
[adgk] 查找给定集合内的任何字符。
[^adgk] 查找给定集合外的任何字符。
(red|blue|green) 查找任何指定的选项。

元字符

元字符(Metacharacter)是拥有特殊含义的字符。

表1.常用的元字符

代码 说明
. 匹配除回车\r 、换行\n 、行分隔符\u2028 和段分隔符\u2029 以外的任意单个字符。
^ 匹配字符串的开始。比如 /^test/ 就是匹配以test开始的字符串
$ 匹配字符串的结束。比如/test$/就是匹配以test结束的字符串。
/^test$/就是表示从开始位置到结束位置只有test。符合这个正则的只有test字符串。
| 选择符。表示“或”关系。比如x|y 匹配x或y
\b 匹配单词的边界(开始或结束)。比如: /\bt/匹配“test”中的’m‘;/es\b/并不匹配“test”中的’es‘,因为es后面被词汇t跟着,不是单词结束的边界。/est\b/匹配“test”中的’est‘,因为est是这个字符串的结束部分。
\B 匹配一个非单词边界。即在词的内部。
\d 匹配一个数字。相当于[0-9]
\D 匹配一个非数字字符。相当于[^0-9]
\w 匹配任意一个单字字符(字母、数字、下划线),相当于[A-Za-z0-9_]
\W 匹配一个非单字字符(除所有字母、数字和下划线以外的字符),相当于[^A-Za-z0-9_]
\s 匹配一个空白字符,包括空格、制表符、换页符和换行符。相当于[\t\r\n\v\f]
\S 匹配非空格的字符,相当于[^\t\r\n\v\f]
\0 查找 NUL 字符。
\n 查找换行符。
\f 查找换页符。
\r 查找回车符。
\t 查找制表符。
\v 查找垂直制表符。
\xxx 查找以八进制数 xxx 规定的字符。
\xdd 查找以十六进制数 dd 规定的字符。
\uxxxx 查找以十六进制数 xxxx 规定的 Unicode 字符。

量词

量词 描述
n+ 匹配任何包含至少一个 n 的字符串。
n* 匹配任何包含零个或多个 n 的字符串。
n? 匹配任何包含零个或一个 n 的字符串。
n{X} 匹配包含 X 个 n 的序列的字符串。
n{X,Y} 匹配包含 X 至 Y 个 n 的序列的字符串。
n{X,} 匹配包含至少 X 个 n 的序列的字符串。
n$ 匹配任何结尾为 n 的字符串。
^n 匹配任何开头为 n 的字符串。
?=n 匹配任何其后紧接指定字符串 n 的字符串。
?!n 匹配任何其后没有紧接指定字符串 n 的字符串。

相关链接

文章目录
  1. 1. 正则表达式是个什么东东
    1. 1.1. 利用图形化工具理解正则表达式
  2. 2. 正则表达式语法
    1. 2.1. RegExp 对象
    2. 2.2. 正则对象的属性和方法
      1. 2.2.1. 属性
      2. 2.2.2. 方法
        1. 2.2.2.1. test()
        2. 2.2.2.2. exec()
    3. 2.3. 字符串对象的方法
      1. 2.3.1. 方法
        1. 2.3.1.1. String.prototype.match()
        2. 2.3.1.2. String.prototype.search()
        3. 2.3.1.3. String.prototype.replace()
        4. 2.3.1.4. String.prototype.split()
    4. 2.4. 匹配规则
      1. 2.4.1. 字面量字符(原义字符)
      2. 2.4.2. 方括号
      3. 2.4.3. 元字符
      4. 2.4.4. 量词
  3. 3. 相关链接
|