JavaScript 正则

语法
/pattern/modifiers;

实例
var patt = /w3cschool/i

实例解析:
/w3cschool/i 是一个正则表达式。
w3cschool 是一个模式 (用于检索)。
i 是一个修饰符 (搜索不区分大小写)。

一、基础元字符

字符 含义 示例
. 任意单个字符(除换行) a.cabc, a1c
\d 数字 [0-9] \d{3}123
\D 非数字 [^0-9] \D+abc
\w 单词字符 [a-zA-Z0-9_] \w+hello_world
\W 非单词字符 \W@, #,
\s 空白字符(空格、Tab、换行) \s+
\S 非空白字符 \S+abc
\b 单词边界 \bword\b 精确匹配 word
\B 非单词边界

二、量词(匹配次数)

量词 含义 等价写法
* 0 次或多次 {0,}
+ 1 次或多次 {1,}
? 0 次或 1 次 {0,1}
{n} 恰好 n 次 \d{3}123
{n,} 至少 n 次 \w{3,}
{n,m} n 到 m 次 \d{3,8}

三、字符类与范围

1
2
3
4
5
6
7
[abc]       // a 或 b 或 c
[^abc] // 非 a、b、c
[a-z] // a 到 z
[A-Z] // A 到 Z
[0-9] // 0 到 9
[a-zA-Z] // 所有字母
[a-zA-Z0-9_] // 等价于 \w

[] 内,大部分特殊字符失去特殊含义

1
2
[.*+?]      // 匹配字面量 . * + ?
[-a-z] // - 放开头或结尾表示字面量

四、边界与位置

符号 含义
^ 字符串开头(多行模式下是行首)
$ 字符串结尾(多行模式下是行尾)
\b 单词边界
\B 非单词边界
(?=...) 正向前瞻
(?!...) 负向前瞻
(?<=...) 正向后顾(ES2018)
(?<!...) 负向后顾(ES2018)
1
2
3
4
5
// 前瞻:匹配后面跟着 "Script" 的单词
/[a-zA-Z]+(?=Script)/g.exec('JavaScript'); // ['Java']

// 后顾:匹配前面是 "$" 的数字(ES2018)
/(?<=\$)\d+/.exec('Price: $100'); // ['100']

五、分组与捕获

语法 含义
(...) 捕获分组
(?:...) 非捕获分组(不保存到结果)
\1, \2 反向引用第 n 个分组
(?<name>...) 命名捕获组(ES2018)
1
2
3
4
5
6
7
let re = /^(?<area>\d{3})-(?<number>\d{3,8})$/;
let result = re.exec('010-12345');
console.log(result.groups.area); // '010'
console.log(result.groups.number); // '12345'

// 反向引用:匹配重复单词
/\b(\w+)\s+\1\b/.test('hello hello'); // true

六、贪婪 vs 非贪婪

模式 符号 行为
贪婪(默认) *, +, ?, {n,m} 尽可能多匹配
非贪婪 *?, +?, ??, {n,m}? 尽可能少匹配
1
2
3
4
let html = '<div>content</div><span>text</span>';

/<.*>/.exec(html)[0]; // '<div>content</div><span>text</span>' 贪婪
/<.*?>/.exec(html)[0]; // '<div>' 非贪婪

七、JavaScript 正则标志(Flags)

| 标志 | 含义 | 说明 |----------- |
| g | global 全局匹配 | exec() 多次调用,更新 lastIndex |
| i | ignoreCase 忽略大小写 | |
| m | multiline 多行模式 | ^$ 匹配每行开头结尾 |
| s | dotAll 单行模式 | . 匹配换行符(ES2018) |
| u | unicode Unicode 模式 | 正确处理 Unicode(ES2015) |
| y | sticky 粘性匹配 | 从 lastIndex 开始严格匹配(ES2015) |

1
2
3
4
5
let re = /test/gi;           // 全局 + 忽略大小写
let re2 = new RegExp('test', 'gi'); // 等价

// dotAll 模式(ES2018)
/foo.bar/s.test('foo\nbar'); // true,. 能匹配 \n

八、JS 正则 API

1. RegExp.prototype.test(str) — 布尔判断

1
2
/^\d+$/.test('123');  // true
/^\d+$/.test('abc'); // false

2. RegExp.prototype.exec(str) — 提取匹配

1
2
3
4
5
6
7
let re = /(\d{3})-(\d{3,8})/;
let result = re.exec('010-12345');
// result[0] = '010-12345' 完整匹配
// result[1] = '010' 第1个捕获组
// result[2] = '12345' 第2个捕获组
// result.index = 0 匹配位置
// result.input = '010-12345' 原始字符串

3. String.prototype.match(regexp) — 匹配数组

1
2
3
4
5
'abc123def456'.match(/\d+/g);  // ['123', '456']

// 无 g 标志时,类似 exec,但返回数组
'010-12345'.match(/(\d{3})-(\d{3,8})/);
// ['010-12345', '010', '12345', index: 0, input: '010-12345', groups: undefined]

4. String.prototype.matchAll(regexp) — 迭代所有匹配(ES2020)

1
2
3
4
5
6
let str = 'JavaScript, VBScript, JScript';
let re = /[a-zA-Z]+Script/g;

for (const match of str.matchAll(re)) {
console.log(match[0]); // 'JavaScript', 'VBScript', 'JScript'
}

5. String.prototype.search(regexp) — 查找位置

1
2
'hello world'.search(/world/);  // 6
'hello world'.search(/xyz/); // -1

6. String.prototype.replace(regexp, replacement) — 替换

1
2
3
4
5
6
7
'2024-06-20'.replace(/-/g, '/');  // '2024/06/20'

// 使用捕获组
'010-12345'.replace(/(\d{3})-(\d{3,8})/, '($1) $2'); // '(010) 12345'

// 使用函数处理
'abc123def456'.replace(/\d+/g, match => `[${match}]`); // 'abc[123]def[456]'

7. String.prototype.split(separator) — 分割

1
'a,b;; c  d'.split(/[\s,;]+/);  // ['a', 'b', 'c', 'd']

九、实用正则大全

Email(基础版)

1
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/

手机号(中国大陆)

1
/^1[3-9]\d{9}$/

身份证号

1
2
/^\d{15}|\d{18}$/           // 简单版
/^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/ // 严格版

URL

1
/^https?:\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(\/\S*)?$/

中文字符

1
2
/[\u4e00-\u9fa5]+/           // 基础中文
/\p{Script=Han}/u // Unicode 属性(ES2018,更完整)

1. [\u4e00-\u9fa5] ([一-龥])与 [\u4e00-\u9fbb]([一-龻])的区别

[\u4e00-\u9fbb] 是第一版 Unicode 中汉字的全部范围,包括了一些生僻字。如果你在处理文本或编程时需要考虑字符集的完整性,应该使用 [\u4e00-\u9fbb]。

[\u4e00-\u9fa5] 是目前比较常用的写法,表示 GBK 编码中除了一些生僻字之外的汉字范围,这也是现在常用的字符集标准GB18030中规定的汉字范围。如果你只需要处理常用汉字,[\u4e00-\u9fa5] 已经足够。

2. 比较广泛的中文汉字。(包含了咱们需要的生僻字 和 不需要的很多字符 比如 中文句号分号逗号、书名号 等等) \u2E80-\uFE4F

3. CJK 标点符号
范围:\u3000-\u303F

密码强度(8-20位,至少包含字母和数字)

1
/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,20}$/

去除 HTML 标签

1
html.replace(/<[^>]+>/g, '')

提取 Markdown 链接

1
/\[([^\]]+)\]\(([^)]+)\)/g   // [text](url)

十、性能优化与陷阱

陷阱 说明 解决
回溯灾难 (a+)+b 匹配超长字符串时指数级回溯 使用原子组(JS 不支持)或简化模式
贪婪导致过度匹配 .* 匹配太多 .*? 非贪婪,或限定范围
Unicode 问题 \w 不匹配中文 [\u4e00-\u9fa5]\p{L}u 标志)
多行匹配 ^$ 默认只匹配首尾 m 标志
lastIndex 陷阱 g 标志的 RegExp 会记住位置 每次 test/exec 前重置 lastIndex = 0
1
2
3
4
5
let re = /\d+/g;
re.test('123'); // true
re.test('123'); // false! 因为 lastIndex 已经到末尾
re.lastIndex = 0; // 重置
re.test('123'); // true

十一、ES 新特性时间线

特性 版本 说明
u (Unicode) ES2015 \u{1F600} 正确匹配 Emoji
y (Sticky) ES2015 粘性匹配
s (dotAll) ES2018 . 匹配换行
命名捕获组 (?<name>) ES2018 result.groups.name
后顾断言 (?<=) (?<!) ES2018 向前/向后查找
matchAll ES2020 返回迭代器,替代 while + exec

十二、一句话总结

正则本质是"模式描述语言":用元字符描述规则,用量词控制次数,用分组提取信息,用标志控制行为。JS 中优先用字面量 /.../,需动态构建时用 new RegExp()。复杂场景(如 Email 完整验证)建议用专业库,正则负责简单高效的匹配。

一个示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let re = /^\d{3}-\d{3,8}$/;
console.log(re.test('010-12345')); // true
console.log(re.test('010-1234x')); // false
console.log(re.test('010 12345')); // false

console.log('a b c'.split(/\s+/)); // ['a', 'b', 'c']

re = /^(\d{3})-(\d{3,8})$/;
console.log(re.exec('010-12345')); // ['010-12345', '010', '12345']
console.log(re.exec('010 12345')); // null

re = /^(\d+)(0*)$/;
console.log(re.exec('102300')); // ['102300', '102300', '']
// 非贪婪匹配
re = /^(\d+?)(0*)$/;
console.log(re.exec('102300')); // ['102300', '1023', '00']

测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<script>
// 期望 false
let reg =
/(^([\u2E80-\uFE4F](?![\u3000-\u303F])){1}((?![\u3000-\u303F])[\u2E80-\uFE4F]|\.|·|。){0,18}([\u2E80-\uFE4F](?![\u3000-\u303F])){1}$)|(^[a-zA-Z]{1}[a-zA-Z\s]{0,18}[a-zA-Z]{1}$)/;
document.write(!reg.test("史飞机a周"));
</script>
</body>
</html>

工具

正则表达式在线测试 | 菜鸟工具

参考