javascript正则表达式

正则表达式一直是一个令人头疼但却又是十分重要的一个东西。熟练的使用正则表达式可以让你的工作事半功倍。接下来,一起来看看正则表达式是什么吧!

正则表达式概念

正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表通常被用来检索、替换那些符合某个模式(规则)的文本。

正则表达式的基本语法

1
var reg = / pattern / flags;

就像上面的,正则表达式是由两个/符号包裹起来的,两个/里面的(上面的pattern)就是需要的任何简单或者是复杂的正则表达式。而在第二个/后面是一个或者是多个标志(flags),用来标明正则表达式的行为。
flags有以下五种行为:

  • g:表示全局(global)模式,即在匹配到第一个后继续匹配
  • i:忽略(ignore)大小写
  • m:便是多行(multiline)模式,即到达一行时继续查找下一行
  • y:(ES6新增的粘连修饰符)
  • u:(ES6新增)

    正则的规则分类

    下面根据JavaScript正则表达式的速查表中的分类来对每一下进行一个说明。

    正则表达式基础

    基础里面呢主要有6需要记忆的东西,分别是:

  • .:匹配除了换行以外其他所有字符

  • a:匹配字符a,衍生为匹配单个字母
  • ab:匹配字符串ab,衍生为匹配字符串
  • a|b:匹配a或者b,就是或者的意思
  • a*:匹配0次或者是多次a
  • \:转义符号,转义一个特殊的字符
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
// `.`的使用
var reg = /./g;
var text = "abcdefg";
var res = text.match(reg);
console.log(res); // (7) ["a", "b", "c", "d", "e", "f", "g"]

// `a`的使用
var reg = /A/gi; // 全局匹配。忽略大小写
var text = "abcdefg";
var res = text.match(reg);
console.log(res); // (7) ["a"]

// `ab`的使用
var reg = /AbCdef/gi; // 全局匹配。忽略大小写
var text = "abcdefg";
var res = text.match(reg);
console.log(res); // (7) ["abcdef"]

// `a|b`的使用 一
var reg = /a|b|c|d/; // 非全局匹配。
var text = "abcdefg";
var res = text.match(reg);
console.log(res); // ["a", index: 0, input: "abcdefg"]

// `a|b`的使用 二
var reg = /a|b|c|d/g; // 全局匹配。
var text = "abcdefg";
var res = text.match(reg);
console.log(res); // (4) ["a", "b", "c", "d"]。使用全局匹配。会在匹配对后继续匹配

// `a*`的使用
var reg = /a*/;
var text = "abcdcba";
var res = text.match(reg);
console.log(res); // ["a", index: 0, input: "abcdcba"]

// `a*`的使用
var reg = /a*/g; // 全局匹配。
var text = "abcdcba";
var res = text.match(reg);
console.log(res); // (8) ["a", "", "", "", "", "", "a", ""]。使用全局匹配会把之后没有匹配到的转化为空字符串

// `\`的使用 回到第一个 . 的使用那里。如果我把 . 前面加一个 \ 符号,那么这个点就是一个普通的点了
var reg = /\./g; // 全局匹配。
var text = "abcdefg";
var res = text.match(reg);
console.log(res); // null

JavaScript中需要使用 \ 的特殊符号有:( ) [ ] { } . | + * ? \ ^ $ 以及 空白

正则表达式字符类

  • [ab-d]:a,b,c,d四个字符中的一个,衍生为使用[],那就是匹配其中的一个
  • [^ab-d]:除了a,b,c,d四个字符其他的任意字符,衍生为使用[^]可以排除[]里面的东西
  • [\b]:退格字符,了解
  • \d:一个0-9的数字
  • \D:一个非数字
  • \s:一个空白字符
  • \S:一个非空白字符
  • \w:匹配一个字母,数字或者是下划线
  • \W:匹配一个除字母,数字,下划线之外的任意字符
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
// `[a-z]`的使用
var reg = /[a-z]/g; // 全局匹配。匹配 a-z 的任意一个,因为是全局匹配。所以会一直找。但是不会有 1234
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // (4) ["a", "b", "c", "d"]

// `[a-z]`的使用
var reg = /[^a-z]/g; // 全局匹配。匹配 除了 a-z 的任意一个。所以是 1 2 3 4。因为是全局匹配。所以会一直找
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // (4) ["1", "2", "3", "4"]

// `[\b]`的使用
var reg = /[\b]/g; // 全局匹配。匹配 \b,当然也可以匹配其他的特殊转义字符,见 正则表达式特殊字符
var text = "abcd\b1234";
var res = text.match(reg);
console.log(res); // (4) ["1", "2", "3", "4"]

// `\d`,`\D`,`\s`,`\S`,`\w`,`\W`的使用
var reg = /\D\d/g; // 全局匹配。匹配一个非数字与一个数字。两者紧靠在一起 注意顺序
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // ["d1"]
// 如果是下面的一个,则匹配 null
var reg = /\D\d/g; // 全局匹配。匹配一个数字与一个非数字。两者紧靠在一起 注意顺序
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // null
// 匹配一个
var reg = /\D\s\w\d/g; // 全局匹配。注意顺序 注意大小写
var text1 = "abcd1234";
var text2 = "abcd _1234";
var res1 = text1.match(reg);
var res2 = text2.match(reg);
console.log(res1); // null
console.log(res2); // ["d _1"]

正则表达式量词

  • *:匹配0次或者多次。等价于{0,}
  • +:匹配一次或者多次。等价于{1,}
  • ?:匹配0次或者一次。等价于{0,1}
  • {2}:只匹配2
  • {2, 5}:匹配2-5
  • {2,}:匹配2次或者多次

这里涉及到一个贪婪匹配非贪婪匹配
贪婪匹配指的是使用以上量词的时候会按照最大次数进行匹配。
非贪婪匹配则是按最小次数进行匹配。

使用非贪婪匹配只需在两次后面加上一个?

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
// `*`的使用
// 非全局匹配。
var reg = /[a-z]*/; // 匹配 a-z 的0个或者多个
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // ["abcd", index: 0, input: "abcd1234"]
// 全局匹配。
var reg = /[a-z]*/g; // 匹配 a-z 的0个或者多个
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // (6) ["abcd", "", "", "", "", ""]。当匹配为 0 个的时候,会变成空字符串。对比 + 号

// `+`的使用
// 非全局匹配。
var reg = /[a-z]+/; // 匹配 a-z 的0个或者多个
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // ["abcd", index: 0, input: "abcd1234"]
// 全局匹配。
var reg = /[a-z]+/g; // 匹配 a-z 的0个或者多
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // ["abcd"]。因为至少匹配一个,所以不会有空字符串。对比 * 号

// `?`的使用
// 非全局匹配。
var reg = /[a-z]?/; // 匹配 a-z 的0个或者一个
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // ["a", index: 0, input: "abcd1234"]。匹配到一个就停下来
// 全局匹配。
var reg = /[a-z]?/g; // 匹配 a-z 的0个或者一个
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // ["a", "b", "c", "d", "", "", "", "", ""]。abcd匹配到了。1234则是空字符串。

以上说的都属于贪婪匹配。都是按最多个匹配的。如果是非贪婪匹配。则是按照最少的次数匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// `*`的使用
// 非全局匹配。贪婪匹配
var reg = /[a-z]*/; // 匹配 a-z 的0个或者多个
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // ["abcd", index: 0, input: "abcd1234"]
// 全局匹配。贪婪匹配
var reg = /[a-z]*/g; // 匹配 a-z 的0个或者多个
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // (6) ["abcd", "", "", "", "", ""]。当匹配为 0 个的时候,会变成空字符串。对比 + 号

// 非全局匹配。非贪婪匹配
var reg = /[a-z]*?/; // 匹配 a-z 的0个或者多个
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // ["", index: 0, input: "abcd1234"]
// 全局匹配。非贪婪匹配
var reg = /[a-z]*?/g; // 匹配 a-z 的0个或者多个
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // (9) ["", "", "", "", "", "", "", "", ""]

以上就是贪婪匹配非贪婪匹配的区别。不过不管是如何。在匹配是0次的时候。空字符串的个数总是匹配不成功部分的字符串的长度+1

正则表达式捕获组与非捕获组

  • (...):捕获组
  • (?...):非捕获组
  • \Y:匹配第Y个被捕获的组(也称反向引用)。其中Y是一个数字,这个数组取值范围是*,即{0,}。但是建议反向引用不要索引大于9的捕获性分组。
    捕获组
    1
    2
    3
    4
    var reg = /ab(cd(1234))/;
    var text = "abcd1234";
    var res = text.match(reg);
    console.log(res); // (3) ["abcd1234", "cd1234", "1234", index: 0, input: "abcd1234"]

分析:

  1. 捕获组就是在匹配 ()外面的字符之后在匹配 () 里面的。结果中的 “abcd1234”
  2. 捕获组里面还有捕获组会先忽略里面的捕获组匹配在一次匹配里面的捕获组。结果中的"cd1234""1234"
非捕获组
1
2
3
4
var reg = /ab(?:cd(1234))/;
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // (3) ["abcd1234", "1234", index: 0, input: "abcd1234"]

分析:非捕获组就是不捕获,就行上面的结果中没有了"cd1234"

那么捕获与非捕获有什么区别呢? 记得看宠物小精灵的时候小智看见新的小精灵扔球抓小精灵的时候就属于一个捕获,如果看见不扔球那就是不捕获。那捕获和不捕获有什么区别呢?就行抓小精灵一样,抓住了之后就可以用了嘛!所以被捕获的东西之后是可以使用的。那么怎么使用呢? 问得好。这时候就需要使用到\Y 了。

1
2
3
4
5
6
7
8
var reg1 = /(a)/;
var reg2 = /(a)\1/; // 相当于是 /(a)a/
var text1 = "aabb1234";
var text2 = "aabb1234";
var res1 = text1.match(reg1);
var res2 = text2.match(reg2);
console.log(res1); // ["a", "a", index: 0, input: "aabb1234"]
console.log(res2); // ["aa", "a", index: 0, input: "aabb1234"]

上面的例子中,reg1reg2 仅仅有一点不同。但是匹配后的东西是不同的。简单地说,就是,使用 \Y 后会赋值第Y个捕获的组。以下代码说明通过$Y来接收相应的捕获组。不能从0开始

\Y
1
2
3
4
5
6
7
8
9
var reg = /ab(cd(1234))/;
var text = "abcd1234";
var res = text.match(reg);
console.log(res); // (3) ["abcd1234", "cd1234", "1234", index: 0, input: "abcd1234"]
console.log(RegExp.$0); // undefined
console.log(RegExp.$1); // cd1234
console.log(RegExp.$2); // 1234
var res1 = text.replace(reg,"$1$2"); // 使用 $1 $2 取出捕获组的内容
console.log(res1); // cd12341234

正则表达式断言

  • ^:字符串的开始必须是
  • $:字符串的结尾必须是
  • \b:匹配文字(单词)边界
  • \B:非文字(单词)边界
  • (?=...):积极的前瞻(也叫前瞻或者是顺序肯定环视)
  • (?!...):消极的前瞻(也加后瞻或者是顺序否定环视)
^$的使用
1
2
3
4
5
6
7
8
9
10
var reg = /^b.*\d$/; // 要求字符 b 开头,中间 * 个任意字符(除换行符),必须以数字结束
var text1 = "abcd1234";
var text2 = "bcd";
var text3 = "bcd1234";
var res1 = text1.match(reg);
var res2 = text2.match(reg);
var res3 = text3.match(reg);
console.log(res1); // null
console.log(res2); // null
console.log(res3); // ["bcd1234", index: 0, input: "bcd1234"]
\b\B的使用
1
2
3
4
5
6
7
8
9
10
var reg = /\bhi\b/; // 
var text1 = "hi";
var text2 = "him";
var text3 = "history";
var res1 = text1.match(reg);
var res2 = text2.match(reg);
var res3 = text3.match(reg);
console.log(res1); // ["hi", index: 0, input: "hi"]
console.log(res2); // null
console.log(res3); // null
积极的前瞻 (?=…) 积极的前瞻 匹配后面是 … 的位置,不捕获匹配结果
1
2
3
4
5
6
7
var reg = /abcd(?=1234)/;
var text = "abcd1234";
var text1 = "abcdefg";
var res = text.match(reg);
var res1 = text1.match(reg);
console.log(res) // ["abcd", index: 0, input: "abcd1234"]
console.log(res1) // null

看上面的例子可以看出积极的前瞻匹配1234前面的abcd,不匹配def前面的abcd;而消极的前瞻恰恰相反,看下面的例子。

消极的前瞻 (?!…) 消极的前瞻 匹配后面不是 … 的位置,不捕获匹配结果
1
2
3
4
5
6
7
var reg = /abcd(?!=1234)/;
var text = "abcd1234";
var text1 = "abcdefg";
var res = text.match(reg);
var res1 = text1.match(reg);
console.log(res) // null
console.log(res1) // ["abcd", index: 0, input: "abcd1234"]

正则表达式特殊字符

  • \n:换行符
  • \r:回车符
  • \t:制表符
  • \0:空字符
  • \YYY:8进制字符
  • \xYY:十六进制字符
  • \uYYYY:十六进制字符
  • \cY:控制符
    正则的特殊字符在实际中运用的比较少,具体的用法与之前讲到的[\b]类似。上面的两个十六进制字符中,\xYY主要匹配数字字母等。而\uYYYY则是为了匹配汉字以及以下特殊的符号。

正则表达式替换

正则表达式替换主要是替换一些字符。主要以下几个,可是在replace中使用。

  • $$:插入$
  • $&:插入整个匹配
  • $`:插入匹配项前面的字符串
  • $':插入匹配项后面的字符串
  • $Y:插入第Y个捕获的组
1
2
3
4
var reg = /.{2}水(.)/;
var text = "君子之交淡如水,小人之交甘若醴";
var res = text.match(reg);
console.log(res); // (2) ["淡如水,", ",", index: 4, input: "君子之交淡如水,小人之交甘若醴"]
$$:插入$
1
2
3
4
var reg = /.{2}水(.)/;
var text = "君子之交淡如水,小人之交甘若醴";
var newStr = text.replace(reg,"$$");
console.log(newStr); // 君子之交$小人之交甘若醴
$&:插入整个匹配
1
2
3
4
var reg = /.{2}水(.)/;
var text = "君子之交淡如水,小人之交甘若醴";
var newStr = text.replace(reg,"$&");
console.log(newStr); // 君子之交淡如水,小人之交甘若醴
$`:插入匹配项前面的字符串
1
2
3
4
var reg = /.{2}水(.)/;
var text = "君子之交淡如水,小人之交甘若醴";
var newStr = text.replace(reg,"$`");
console.log(newStr); // 君子之交君子之交小人之交甘若醴
$':插入匹配项后面的字符串
1
2
3
4
var reg = /.{2}水(.)/;
var text = "君子之交淡如水,小人之交甘若醴";
var newStr = text.replace(reg,"$'");
console.log(newStr); // 君子之交小人之交甘若醴小人之交甘若醴
$Y:插入第Y个捕获的组
1
2
3
4
var reg = /.{2}水(.)/;
var text = "君子之交淡如水,小人之交甘若醴";
var newStr = text.replace(reg,"$1");
console.log(newStr); // 君子之交,小人之交甘若醴

正则表达式实例的方法

exec() 最主要的方法

此方法专门为捕获组设计的。此方法接收一个参数,及需要测试的字符串。返回数组或者是null。但返回的值包含两个额外的属性:
index:匹配性在字符串中的位置
input:应用正则的表达式

1
2
3
4
var reg = /你(我他(与她))/
var text = "你我他与她";
var res = reg.exec(text);
console.log(res); //(3) ["你我他与她", "我他与她", "与她", index: 0, input: "你我他与她"]

["你我他与她", "我他与她", "与她", index: 0, input: "你我他与她"]的结果。使用res[下标]可以查看具体的匹配下。从第二项开始才是捕获项。使用res.indexres.input查看起始位置与匹配的字符串。

exec()方法始终返回一项,如果设置了全局匹配g,只会从上一次结束的敌法继续查找,而不会返回多项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var text = "bat, cat, fat";

var reg1 = /.at/;
var res = reg1.exec(text);
console.log(res.index); // 0
console.log(res[0]); // bat

var res = reg1.exec(text);
console.log(res.index); // 0
console.log(res[0]); // bat

var reg2 = /.at/g;

var res = reg2.exec(text);
console.log(res.index); // 0
console.log(res[0]); // bat

var res = reg2.exec(text);
console.log(res.index); // 5
console.log(res[0]); // cat

test()方法

接收一个字符串作为参数,返回:
true:匹配
false:不匹配

1
2
3
4
5
6
7
var text = "abcd1234";
var reg1 = /\D/g;
var reg2 = /\s/g;
var res1 = reg1.test(text);
console.log(res1); // true
var res2 = reg2.test(text);
console.log(res2); // false

关于使用RegExp构造函数

语法:var reg = new RegExp(参数1[,参数2]);

1
2
3
4
var reg = new RegExp("\D","gi");
var text = "ABd1234";
var res = reg.exec(text);
console.log(res); // ["d", index: 2, input: "ABd1234"]

看上面的例子。两个参数需要使用字符串
但是,上面的例子有一个问题。没有按照我们所想的出现一个A,而是一个d。同时,我们忽略大小写在看一下:

1
2
3
4
var reg = new RegExp("\D","g");
var text = "ABd1234";
var res = reg.exec(text);
console.log(res); // null

所以,使用构造函数还有一个需要注意的就是所有的元字符都需要双重转义。将上面的代码换为下面的看看

1
2
3
4
var reg = new RegExp("\\D","g");
var text = "ABd1234";
var res = reg.exec(text);
console.log(res); // ["A", index: 0, input: "ABd1234"]

注意上面的\\D。这个才表示一个元字符。刚刚的就是一个\一个D。所以。在正则的构造函数中使用元字符需要双重转义

附上几个常用的正则匹配

电话号码匹配:/^1[3-8]\d{9}$/
电子邮件:/[a-zA-z0-9_-]{6,12}@[a-zA-z0-9_-]+\.[a-zA-z0-9]+/
匹配特定了的邮件:/[a-zA-z0-9_-]{6,12}@(163|qq|gmail)\.com/

更多的匹配可查阅:最全的常用正则表达式大全

以上就是关于正则的一些使用概念以及使用方法。如果想要熟练的使用正则去匹配或者是修改字符串。还是需要不断的练习才是。

文章作者: 踏浪
文章链接: https://www.lyt007.cn/技术/javascript正则表达式.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 踏浪
支付宝
微信打赏