Hellom's Studio.

巧记正则表达式

字数统计: 2.7k阅读时长: 11 min
2019/09/02 Share

在线正则表达式工具

RegExp对象

Js 中通过内置 RegExp 对象来支持正则表达式

通过以下两种来实例化 RegExp 对象:

  • 字面量

  • 构造函数

字面量

1
2
3
4
5
6
7
e.g.

const reg = /\bis\b/g;

const str = 'He is a boy; This is a dog ; Where is she ?';

console.log(str.replace(reg,'IS'));// He IS a boy; This IS a dog ; Where IS she ?

构造函数

1
2
3
4
5
6
7
e.g.

const reg = new RegExp('\\bis\\b','g');

const str = 'He is a boy; This is a dog ; Where is she ?';

console.log(str.replace(reg,'IS'));

修饰符

  • g: global 全文搜索 若不添加 匹配到第一个就结束

  • i: ignore case 忽略大小写 默认大小写敏感

  • m: multiple lines 多行搜索

元字符

js 中由两种基本的字符类型组成:

  • 原义文本字符

  • 元字符

元字符是在正则表达式中有特殊含义的非字母字符

例如: *,^,+,?,$,.,|,\,(),{},[]

字符类

一般情况下 正则表达式一个字符对应字符串的一个字符

可使用 [] 来构建一个简单的类

所谓类就是符合某种特征的对象 一个泛指 而不是特指某个字符

表达式 [abc] 把字符 a 或 b 或 c 归为一类 表达式可匹配这类的字符

1
2
3
4
5
6
7
e.g.

const reg = /[abc]/g;

const str = 'a1b2c3d4';

console.log(str.replace(reg,'X')); // X1X2X3d4

字符类取反

通常使用 元字符 ^ 来创建一个 反向类/负向类

反向类 是指不属于某个类的内容

表达式 [^abc] 表示不是 a 或 b 或 c 的内容

1
2
3
4
5
6
7
e.g.

const reg = /[^abc]/g;

const str = 'a1b2c3d4';

console.log(str.replace(reg,'X')); // aXbXcXXX

范围类

正则表达式还提供了 范围类

可使用 [a-z] 来连接两个字符来表示 从 a 到 z 的任意字符

这是一个闭区间 包含 a 和 z 本身

[] 组成的类内部是可以连写的 例如: [a-zA-Z]

1
2
3
4
5
6
7
e.g.

const reg = /[a-zA-Z]/g;

const str = 'a1b2c3d4QQASDASDAS';

console.log(str.replace(reg,'X')); // X1X2X3X4XXXXXXXXXX

JS预定义类及边界

正则表达式提供了 预定义类 来匹配常见的字符类

字符 等价类 含义
. [^\r\n] 除了回车符和换行符之外的所有字符
\d [0-9] 数字字符
\D [^0-9] 非数字字符
\s [\t\n\x0B\f\r] 空白符
\S [^\t\n\x0B\f\r] 非空白符
\w [a-zA-Z_0-9] 单词字符(字母数字下划线)
\W [^a-zA-Z_0-9] 非单词字符

d: digit

s: space

w: word

大写字母表取反

例子:匹配一个 ab + 数字 + 任意字符 的字符串

1
2
3
4
5
6
7
e.g.

const reg = /ab[0-9][^\r\n]/;

等价于 写起来也简单许多

const reg = /ab\d./;

边界

正则表达式还提供了 常用的边界匹配字符

字符 含义
^ 从xxx开始
$ 从xxx结束
\b 单词边界
\B 非单词边界
1
2
3
4
5
6
7
8
9
10
11
12
13
e.g

// 匹配单词边界

const reg = /is/g;

const str = 'This is a boy';

console.log(str.replace(reg,'X')); // ThX X a boy => error

const reg = /\bis\b/g;

console.log(str.replace(reg,'X')); // This X a boy => right
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
e.g

// 匹配所有以@开头 并替换为 X

const reg = /^@\d/g;

const str = `@123
@456
@789`;

console.log(str.replace(reg,'X')); => error
// X23
// @456
// @789

const reg = /^@\d/gm;

console.log(str.replace(reg,'X')); => right
// X23
// X56
// X89

量词

字符 含义
? 出现 0 次或者一次(最多出现一次)
+ 出现一次或多次 (至少一次)
* 出现 0 次或者多次 (任意次)
{n} 出现 n 次
{n,m} 出现 n 到 m 次
{n,} 至少出现 n 次
1
2
3
e.g.

const reg = /\d{3}/; => 匹配三次数字

贪婪模式与非贪婪模式

贪婪模式

  • 就是在匹配成功的前提下 尽可能多的去匹配
1
2
3
4
5
6
7
e.g.

const reg = /\d{3,6}/g;

const str = `12345678`;

console.log(str.replace(reg,'X')); // X78

非贪婪模式

  • 就是在匹配成功的前提下 尽可能少的去匹配

  • 非贪婪模式 可使用 ? 来表示

1
2
3
4
5
6
7
e.g.

const reg = /\d{3,6}?/g;

const str = `12345678`;

console.log(str.replace(reg,'X')); // XX78

分组

  • 可使用 () 来分组 使量词作用于分组
1
2
3
4
5
6
7
8
9
e.g.

//匹配字母与数字组合出现三次 替换为 X

const reg = /(\w\d){3}/;

const str = `a1b2c3d4`;

console.log(str.replace(reg,'X')); // Xd4

  • 可使用 | 来匹配其中一个
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
e.g.

const reg = /boy|girl/g;

const str = `boygirl`;

console.log(str.replace(reg,'X')); // XX


// 分组 或
const reg = /b(oy|gi)rl/g;

const str = `boyrlbgirl`;

console.log(str.replace(reg,'X')); // XX

反向引用

  • 如果出现多个分组 可使用 $ 来表示第几个分组

实例: 2019-09-08 替换成 09/08/2019

1
2
3
4
5
6
7
e.g.

const reg = /(\d{4})-(\d{2})-(\d{2})/g;

const str = `2019-09-08`;

console.log(str.replace(reg,'$2/$3/$1')); // 09/08/2019

忽略分组

  • 不希望捕获某些分组 只需要在分组内添加 ?: 即可忽略分组
1
2
3
e.g.

const reg = /(?:boy).(ok)/;

前瞻

  • 正则表达式从文本头部向文本尾部开始解析,文本尾部方向 为 “前”

  • 前瞻 就是在正则表达式匹配规则的时候,向前检查是否符合断言(断言就是前面语法的一部分)

  • 后顾/后瞻 方向相反

  • js 不支持 后瞻

  • 符合和不符合特定的断言 称为 肯定/正向匹配 和 否定/负向匹配

名称 正则
正向前瞻 exp(?= assert)
负向前瞻 exp(?! assert)
正向后瞻 js不支持
负向后瞻 js不支持

正向前瞻

1
2
3
4
5
6
7
e.g.

const reg = /\w(?=\d)/g;

const str = `a2*34v8`;

console.log(str.replace(reg,'X')); // X2*X4X8

反向前瞻

1
2
3
4
5
6
7
e.g.

const reg = /\w(?!\d)/g;

const str = `a2*34v8`;

console.log(str.replace(reg,'X')); // aX*3XXX

对象属性

global: 是否全文搜索,默认false

ignore case: 是否大小写敏感,默认false

muliline: 多行搜索,默认false

lastIndex: 是当前正则表达式匹配内容的最后一个字符的下一个位置

source: 正则表达式的文本字符串

test 方法

RegExp.prototype.test(str)

以下的事例 出现了这样的情况

多打印几次后 从原来的 true 变成了 false

这是为什么呢?

这是因为 lastIndex 这个属性

当它下一次执行的时候 并不是从头开始匹配的

而是从匹配最后一个字符的下一个位置

也就是 第一次是匹配到 a 值为 true

第二次匹配的时候 从 a 的下一个位置

也就是 b 匹配到了 值为 true

第三次 从 b 的下一个位置 匹配

因而后面 没有可匹配的 所以值为 false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
e.g.

var reg1 = /\w/;
var reg2 = /\w/g;

console.log(reg1.test('ab')); // true

console.log(reg2.test('ab')); // true
console.log(reg2.test('ab')); // true
console.log(reg2.test('ab')); // false => ???


while(reg2.test('ab')){
console.log(reg2.lastIndex);
// result:
// 1
// 2
}

exec 方法

RegExp.prototype.exec(str)

  • 使用正则表达式模式对字符串执行搜索,并将更新全局的 RegExp 对象的属性以反映匹配结果

  • 如果没有匹配的文本 则返回 null 否则返回一个结果数组:

    • index: 匹配文本的第一个位置

    • input: 用来存放被检索的字符串 string

非全局调用

  • 调用非全局的 RegExp 的 exec() 返回一个数组

  • 第一个元素是与正则表达式匹配的文本

  • 第二个元素是与 RegExpObject 的第一个子表达式相匹配的文本(如果有的话)

  • 第三个元素是与 RegExp 对象的第二个子表达式相匹配的文本(如果有的话)(也就是分组)

  • 以此类推

以下的实例:

非全局调用的结果: 0 1 1az2,a,z

  • 0: lastIndex 的值

  • 1: 匹配文本的第一个位置

  • 1az2: 被检索到的字符串

  • a: 第一个分组的值 (\w)

  • z: 第二个分组的值 (\w)

全局调用的结果:

5 1 1az2,a,z

10 6 3cy4,c,y 同理

  • 5: 匹配最后一个字符的下一个位置(下标值) 也就是 b

  • 1: 匹配文本的第一个位置(下标值)

  • 1az2: 被检索到的字符串

  • a: 第一个分组的值 (\w)

  • z: 第二个分组的值 (\w)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
e.g.


var reg3 = /\d(\w)(\w)\d/;

var reg4 = /\d(\w)(\w)\d/g;

var str = '$1az2b3cy4d5e';

var res = reg3.exec(str);

// 非全局调用
console.log(`${reg3.lastIndex} \t ${res.index} ${res.toString()}`);


// 全局匹配调用

while(res = reg4.exec(str)){
console.log(`${reg4.lastIndex} \t ${res.index} ${res.toString()}`);
}

字符串对象方法

search 方法

  • 用于检索字符串中指定的子字符串 或者检索与正则表达式匹配的子字符串

  • 返回 第一个匹配结果 index,查找不到则返回 -1

  • search 方法 不全局匹配 忽略 g 标志,总是从字符串的开始进行检索

1
2
3
4
5
e.g.

var str = '1az2b3cy4d5e';

console.log(str.search(1)); // 0

match 方法

  • 用于检索字符串,以找到一个或者多个于regexp匹配的文本

  • 有无 g 标志 差距很大

非全局调用:

  • match 方法只能在字符串中执行一次

  • 如果没有找到匹配的结果 则返回 null

  • 否则返回一个数组,其中存放了与它相匹配的文本信息

  • 返回数组的一个元素存放的是匹配的文本,而其余的元素存放的是与正则表达式的子表达式匹配的文本

  • 除了常规的数组元素,返回的数组还含有2个对象属性:

    • index 声明匹配文本的起始字符在字符串的位置

    • input 声明对 stringObject 的引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
e.g.


var reg3 = /\d(\w)\d/;

var reg4 = /\d(\w)\d/g;

var str = '$1a2b3cy4d5e';

var res = str.match(reg4);

// 非全局调用
console.log(res); // [ '1a2', 'a' ]
console.log(` ${res.index} \t ${reg3.lastIndex}`); // 1 0

全局调用:

  • 具有 g 标志的 match 方法将执行全局检索 找到字符串中的所有匹配子字符串

  • 没有找到匹配的子字符串,则返回 null

  • 如果找到了一个或者多个匹配子串,则返回一个数组

  • 数组中存放的是字符串中所有匹配子串,而且也没有 index 或者 input 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
e.g.

var reg3 = /\d(\w)\d/;

var reg4 = /\d(\w)\d/g;

var str = '$1a2b3cy4d5e';

var res = str.match(reg4);

// 全局调用
console.log(res); // [ '1a2', '4d5' ]
console.log(` ${res.index} \t ${reg4.lastIndex}`); // undefined 0

split 方法

  • 把字符串分割成字符数组
1
2
3
e.g.

'a,b,c,d'.split(','); // ['a','b','c','d']
  • 在一些比较复杂的分割情况下 可以使用正则表达式解决
1
2
3
e.g.

'a1b2c3d4'.split(/\d/); // ['a','b','c','d']

replace 方法

三种传值的方法:

  • String.prototype.replace(str, replaceStr);
  • String.prototype.replace(reg, replaceStr);
  • String.prototype.replace(reg, function);
1
2
3
4
5
6
7
e.g.

String.prototype.replace(str,replaceStr);
'a1b'.replace('1',2); // 'a2b'

String.prototype.replace(reg,replaceStr);
'a1b1c1'.replace(/1/g, 2); // 'a2b2c2'

function会在每次匹配替换的时候调用,有四个参数:

  • 匹配字符串

  • 正则表达式分组内容,没有分组则没有该参数(有几个分组 就有几个分组参数)

  • 匹配项在字符串的 index

  • 原字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
e.g.

// 1. 要得到替换成以下的内容
'a1b2c3d4' => 'a2b3c4d5'

'a1b2c3d4'.replace(/\d/g,function(match,index,origin){
return parseInt(match) + 1;
})


// 2. 匹配 (数字 字符 数字)后 把中间的字符去掉

'a1b2c3d4e5'.replace(/(\d)(\w)(\d)/g,function(match,index,group1,group2,group3,origin){
return group2 + group3; // 'a12c34e5'
})
CATALOG
  1. 1. RegExp对象
    1. 1.1. 字面量
    2. 1.2. 构造函数
    3. 1.3. 修饰符
  2. 2. 元字符
  3. 3. 字符类
    1. 3.1. 字符类取反
  4. 4. 范围类
  5. 5. JS预定义类及边界
  6. 6. 边界
  7. 7. 量词
  8. 8. 贪婪模式与非贪婪模式
    1. 8.1. 贪婪模式
    2. 8.2. 非贪婪模式
  9. 9. 分组
    1. 9.1.
    2. 9.2. 反向引用
    3. 9.3. 忽略分组
  10. 10. 前瞻
  11. 11. 对象属性
    1. 11.1. test 方法
    2. 11.2. exec 方法
    3. 11.3. 非全局调用
  12. 12. 字符串对象方法
    1. 12.1. search 方法
    2. 12.2. match 方法
    3. 12.3. split 方法
    4. 12.4. replace 方法