DevToolbox
All articles

正则表达式完整教程:从入门到实战

· 9 min read

正则表达式(Regular Expression,简称 regex 或 regexp)是处理文本的强大工具,掌握它能让表单验证、日志分析、数据清洗等任务事半功倍。本文从语法基础出发,结合中国开发场景的实战示例,帮你系统建立正则表达式的知识体系。

正则表达式是什么及适用场景

正则表达式是描述字符串匹配规则的模式语言。它用简洁的符号定义"什么样的字符串算匹配",然后由正则引擎执行匹配、提取、替换等操作。

常见使用场景:

  • 表单验证:验证手机号、身份证号、邮箱格式是否合法
  • 文本搜索与替换:在编辑器中批量修改代码,如将所有 var 替换为 let
  • 日志分析:从服务器日志中提取 IP 地址、错误码、响应时间
  • 数据清洗:去除字符串首尾空格、过滤 HTML 标签、标准化日期格式
  • 路由匹配:Web 框架中匹配 URL 路径并提取参数

字符类:匹配"什么样的字符"

字符类定义了在某个位置可以出现哪些字符。

元字符(预定义字符类):

  • .:匹配任意单个字符(除换行符 \n 外)
  • \d:匹配数字,等价于 [0-9]
  • \D:匹配非数字,等价于 [^0-9]
  • \w:匹配字母、数字或下划线,等价于 [a-zA-Z0-9_]
  • \W:匹配非字母、数字、下划线
  • \s:匹配空白字符(空格、制表符、换行符等)
  • \S:匹配非空白字符

自定义字符类(方括号):

  • [abc]:匹配 a、b 或 c 中的任意一个
  • [a-z]:匹配小写字母 a 到 z 中的任意一个
  • [0-9a-fA-F]:匹配十六进制字符
  • [^abc]:匹配除 a、b、c 之外的任意字符(^ 在方括号内表示取反)
// 示例
/\d/.test('abc3');     // true,字符串中含有数字
/[aeiou]/.test('sky'); // false,不含元音字母
/[^0-9]/.test('123');  // false,全是数字,没有非数字字符

锚点:限定匹配的位置

锚点不匹配字符,而是匹配字符串中的特定位置。

  • ^:匹配字符串的开头(在方括号外部)
  • $:匹配字符串的结尾
  • \b:匹配单词边界(单词字符与非单词字符之间的位置)
  • \B:匹配非单词边界
// ^ 和 $ 的重要性
/\d+/.test('abc123def');   // true,中间含有数字
/^\d+$/.test('abc123def'); // false,不是纯数字字符串
/^\d+$/.test('123456');    // true,整个字符串都是数字

// \b 单词边界
'Hello World'.match(/\bWorld\b/);  // 匹配 "World"
'HelloWorld'.match(/\bWorld\b/);   // 不匹配,World 没有独立的边界

量词:控制匹配的次数

量词跟在字符或字符类后面,指定该元素出现的次数。

  • *:匹配 0 次或多次(可以没有)
  • +:匹配 1 次或多次(至少有一个)
  • ?:匹配 0 次或 1 次(可选)
  • {n}:恰好匹配 n 次
  • {n,}:至少匹配 n 次
  • {n,m}:匹配 n 到 m 次(含边界)

贪婪模式 vs 非贪婪模式:

默认情况下,量词是贪婪的,会尽可能多地匹配字符。在量词后面加 ? 变为非贪婪(懒惰)模式,尽可能少地匹配。

const html = '<b>粗体</b> 普通 <b>又一个</b>';

// 贪婪:从第一个 <b> 一直匹配到最后一个 </b>
html.match(/<b>.*<\/b>/);
// 匹配结果:"<b>粗体</b> 普通 <b>又一个</b>"

// 非贪婪:每次尽早结束匹配
html.match(/<b>.*?<\/b>/g);
// 匹配结果:["<b>粗体</b>", "<b>又一个</b>"]

分组:组合与捕获

分组用圆括号将多个元素组合在一起,有两种主要形式:

  • 捕获组 (abc):将括号内的匹配结果保存,可通过 $1$2match()[1] 引用
  • 非捕获组 (?:abc):仅用于分组,不保存匹配结果,性能更好
  • 命名捕获组 (?<name>abc):ES2018 引入,通过名称引用更直观
// 捕获组:提取日期的年月日
const dateStr = '今天是 2026-05-16';
const match = dateStr.match(/(\d{4})-(\d{2})-(\d{2})/);
if (match) {
  console.log(match[1]); // "2026"(年)
  console.log(match[2]); // "05"(月)
  console.log(match[3]); // "16"(日)
}

// 命名捕获组:更清晰
const match2 = dateStr.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
console.log(match2.groups.year);  // "2026"
console.log(match2.groups.month); // "05"

// 分组配合量词
/(ab)+/.test('ababab'); // true,"ab" 重复 3 次
/(?:ha)+/.test('hahaha'); // true,非捕获组配合量词

常用正则示例(中国场景)

以下是针对中国开发场景整理的常用正则表达式,可直接用于项目中。

手机号(中国大陆):

const phoneRegex = /^1[3-9]\d{9}$/;

phoneRegex.test('13812345678');  // true
phoneRegex.test('12345678901');  // false,不以 1[3-9] 开头
phoneRegex.test('1381234567');   // false,位数不足

// 说明:
// ^ 和 $ 确保全字符串匹配
// 1 开头,第二位为 3-9(涵盖现有所有运营商号段)
// \d{9} 匹配后续 9 位数字,共 11 位

邮箱地址:

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

emailRegex.test('[email protected]');       // true
emailRegex.test('[email protected]'); // true
emailRegex.test('invalid@');               // false
emailRegex.test('no-at-sign.com');         // false

// 说明:
// [^\s@]+ 匹配至少一个非空白、非@字符
// 这个正则故意保持简单,避免过于严格导致误拒合法邮箱

居民身份证号(18位):

const idCardRegex = /^\d{17}[\dX]$/;

idCardRegex.test('11010119900101001X'); // true(末位为 X)
idCardRegex.test('110101199001010012'); // true(末位为数字)
idCardRegex.test('1101011990010100');   // false(只有 17 位)

// 说明:
// \d{17} 匹配前 17 位数字
// [\dX] 匹配最后一位(数字或大写 X,X 是校验位的特殊值)
// 注意:此正则只验证格式,不验证校验位算法的正确性

中文字符:

const chineseRegex = /[一-龥]/;

chineseRegex.test('Hello');   // false
chineseRegex.test('你好');    // true
chineseRegex.test('Hello世界'); // true

// 匹配纯中文字符串
const pureChineseRegex = /^[一-龥]+$/;
pureChineseRegex.test('你好世界');  // true
pureChineseRegex.test('Hello世界'); // false

// 说明:
// [一-龥] 覆盖了 Unicode 中 CJK 统一汉字区的主要范围(U+4E00 到 U+9FA5)

邮政编码(中国大陆 6 位):

const postcodeRegex = /^\d{6}$/;

postcodeRegex.test('100000'); // true(北京)
postcodeRegex.test('200000'); // true(上海)
postcodeRegex.test('12345');  // false(5位)
postcodeRegex.test('1234567'); // false(7位)

日期格式 YYYY-MM-DD:

const dateRegex = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;

dateRegex.test('2026-05-16'); // true
dateRegex.test('2026-13-01'); // false(月份超出范围)
dateRegex.test('2026-01-32'); // false(日期超出范围)
dateRegex.test('26-5-1');     // false(格式不符)

// 说明:
// (0[1-9]|1[0-2]) 匹配 01-09 或 10-12,确保月份合法
// (0[1-9]|[12]\d|3[01]) 匹配 01-09、10-29 或 30-31

JavaScript 中使用正则的方法

在 JavaScript 中,正则表达式可以与字符串的多个方法配合使用:

test():检测是否匹配(返回布尔值)

const regex = /^\d+$/;
regex.test('12345');  // true
regex.test('123abc'); // false

// 常用于表单验证
function validatePhone(phone) {
  return /^1[3-9]\d{9}$/.test(phone);
}

match():返回匹配结果数组

// 不带 g 标志:返回第一个匹配及捕获组
'2026-05-16'.match(/(\d{4})-(\d{2})-(\d{2})/);
// ["2026-05-16", "2026", "05", "16", index: 0, ...]

// 带 g 标志:返回所有匹配项(无捕获组详情)
'tel: 13812345678, 备用: 15987654321'.match(/1[3-9]\d{9}/g);
// ["13812345678", "15987654321"]

replace():替换匹配内容

// 脱敏手机号:中间四位替换为 ****
'13812345678'.replace(/^(1[3-9]\d)(\d{4})(\d{4})$/, '$1****$3');
// "138****5678"

// 使用函数进行复杂替换
'hello world'.replace(/\b\w/g, char => char.toUpperCase());
// "Hello World"(每个单词首字母大写)

split():按正则分割字符串

// 按一个或多个空白字符分割
'hello   world\ttab\nnewline'.split(/\s+/);
// ["hello", "world", "tab", "newline"]

// 按中英文标点分割
'你好,世界!Hello, World!'.split(/[,。!,!]+/);
// ["你好", "世界", "Hello", "World", ""]

常用标志(Flags):

  • g(global):全局匹配,找出所有匹配项而非仅第一个
  • i(ignoreCase):忽略大小写
  • m(multiline):多行模式,^$ 匹配每行的开头和结尾
  • s(dotAll):使 . 匹配包括换行符在内的所有字符
// 忽略大小写匹配
/hello/i.test('Hello World');  // true

// 全局替换(不加 g 只替换第一个)
'aabbcc'.replace(/b/g, 'X');   // "aaXXcc"
'aabbcc'.replace(/b/, 'X');    // "aaXbcc"(只替换第一个)

小结

正则表达式的学习曲线较陡,但掌握几个核心概念后就能处理大多数实际问题:字符类定义"匹配什么",锚点定义"在哪里匹配",量词定义"匹配几次",分组让你提取和复用匹配结果。遇到复杂的正则,推荐使用 DevToolbox 正则表达式测试工具 实时调试——它能可视化高亮所有匹配位置,并展示每个捕获组的内容,大幅降低调试难度。

Try it free
正则表达式测试工具
100% client-side · no signup · no upload
Open tool →