正则表达式完全教程:从入门到精通
正则表达式(Regular Expression,简称 RegEx)是程序员必备的技能之一。无论是数据验证、文本搜索还是字符串处理,正则表达式都能让你事半功倍。本文将系统地介绍正则表达式,从基础到高级,帮助你真正掌握这一强大工具。
目录
什么是正则表达式
正则表达式是一种描述字符串模式的语言。它可以用来:
- 验证数据格式:检查邮箱、手机号、身份证号等是否符合规范
- 搜索文本:在大量文本中快速找到符合特定模式的内容
- 替换字符串:批量修改文本中的特定部分
- 提取信息:从非结构化文本中提取结构化数据
实际应用场景
// 验证邮箱格式 const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; console.log(emailRegex.test('user@example.com')); // true // 提取URL中的域名 const urlRegex = /https?:\/\/([^\/]+)/; const match = 'https://www.example.com/path'.match(urlRegex); console.log(match[1]); // www.example.com // 替换敏感词 const text = '请联系我,电话:13812345678'; const maskedText = text.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'); console.log(maskedText); // 请联系我,电话:138****5678
基础语法
1. 普通字符
最简单的正则表达式就是普通字符本身:
const regex = /hello/; console.log(regex.test('hello world')); // true console.log(regex.test('Hello world')); // false (大小写敏感)
2. 元字符
正则表达式的强大之处在于元字符(Metacharacters):
| 元字符 | 说明 | 示例 |
|---|---|---|
. | 匹配任意单个字符(除换行符) | a.c 匹配 "abc", "adc" 等 |
^ | 匹配字符串开始 | ^hello 只匹配以 "hello" 开头的字符串 |
$ | 匹配字符串结束 | world$ 只匹配以 "world" 结尾的字符串 |
* | 匹配前面的字符0次或多次 | ab*c 匹配 "ac", "abc", "abbc" 等 |
+ | 匹配前面的字符1次或多次 | ab+c 匹配 "abc", "abbc" 但不匹配 "ac" |
? | 匹配前面的字符0次或1次 | ab?c 匹配 "ac" 或 "abc" |
\d | 匹配数字 [0-9] | \d{3} 匹配三位数字 |
\w | 匹配字母数字下划线 [a-zA-Z0-9_] | \w+ 匹配一个或多个单词字符 |
\s | 匹配空白字符(空格、制表符、换行等) | \s+ 匹配一个或多个空白字符 |
3. 字符类
使用方括号 [] 定义字符类:
// 匹配元音字母 const vowelRegex = /[aeiou]/; console.log(vowelRegex.test('hello')); // true // 匹配数字 const digitRegex = /[0-9]/; console.log(digitRegex.test('abc123')); // true // 匹配不是数字的字符 const nonDigitRegex = /[^0-9]/; console.log(nonDigitRegex.test('123')); // false
4. 量词
控制匹配次数:
// {n}: 精确匹配 n 次 const phoneRegex = /\d{11}/; console.log(phoneRegex.test('13812345678')); // true // {n,m}: 匹配 n 到 m 次 const usernameRegex = /^[a-zA-Z0-9]{3,16}$/; console.log(usernameRegex.test('user123')); // true // {n,}: 至少匹配 n 次 const passwordRegex = /.{8,}/; console.log(passwordRegex.test('pass123')); // false(少于8个字符)
元字符详解
贪婪匹配 vs 非贪婪匹配
默认情况下,量词是贪婪的,会尽可能多地匹配字符:
const html = '<div>content</div>'; // 贪婪匹配:匹配到最后一个 > const greedyRegex = /<.*>/; console.log(html.match(greedyRegex)[0]); // <div>content</div> // 非贪婪匹配:在量词后加 ?,尽可能少地匹配 const lazyRegex = /<.*?>/; console.log(html.match(lazyRegex)[0]); // <div>
分组与捕获
使用圆括号 () 进行分组,并捕获匹配的内容:
// 捕获日期中的年月日 const dateRegex = /(\d{4})-(\d{2})-(\d{2})/; const match = '2024-01-15'.match(dateRegex); console.log(match[1]); // 2024 console.log(match[2]); // 01 console.log(match[3]); // 15 // 非捕获分组:(?:...) const urlRegex = /https?:\/\/(?:www\.)?([^\/]+)/; const url = 'https://www.example.com/path'; const urlMatch = url.match(urlRegex); console.log(urlMatch[1]); // example.com(不包含 www.)
前瞻和后顾
高级匹配技巧:
// 正向前瞻 (?=...):匹配后面跟着特定模式的位置 const regex1 = /\d+(?=px)/; console.log('width: 100px'.match(regex1)[0]); // 100 // 负向前瞻 (?!...):匹配后面不跟特定模式的位置 const regex2 = /\d+(?!px)/; console.log('width: 100em'.match(regex2)[0]); // 100 // 正向后顾 (?<=...):匹配前面是特定模式的位置 const regex3 = /(?<=\$)\d+/; console.log('price: $100'.match(regex3)[0]); // 100 // 负向后顾 (?<!...):匹配前面不是特定模式的位置 const regex4 = /(?<!\$)\d+/; console.log('count: 100'.match(regex4)[0]); // 100
实战案例
案例1:验证表单输入
// 手机号验证(中国大陆) function validatePhone(phone) { const regex = /^1[3-9]\d{9}$/; return regex.test(phone); } // 身份证号验证 function validateIDCard(id) { const regex = /(^\d{15}$)|(^\d{17}(\d|X|x)$)/; return regex.test(id); } // 密码强度验证(至少8位,包含大小写字母和数字) function validatePassword(password) { const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d@$!%*?&]{8,}$/; return regex.test(password); } // 测试 console.log(validatePhone('13812345678')); // true console.log(validateIDCard('110101199001011234')); // true console.log(validatePassword('Abc12345')); // true console.log(validatePassword('weak')); // false
案例2:提取HTML标签内容
function extractHTMLContent(html) { // 提取所有标签的内容(不包括标签本身) const regex = /<[^>]+>([^<]+)<\/[^>]+>/g; const matches = []; let match; while ((match = regex.exec(html)) !== null) { matches.push(match[1]); } return matches; } const html = ` <div>Title</div> <p>Content here</p> <span>Footer</span> `; console.log(extractHTMLContent(html)); // ['Title', 'Content here', 'Footer']
案例3:格式化货币
function formatCurrency(number) { // 添加千位分隔符 return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } console.log(formatCurrency(1234567.89)); // 1,234,567.89 console.log(formatCurrency(1000)); // 1,000
案例4:解析URL参数
function parseQueryString(url) { const regex = /[?&]([^=&]+)=([^&]*)/g; const params = {}; let match; while ((match = regex.exec(url)) !== null) { params[match[1]] = decodeURIComponent(match[2]); } return params; } const url = 'https://example.com?name=John&age=30&city=Beijing'; console.log(parseQueryString(url)); // { name: 'John', age: '30', city: 'Beijing' }
高级技巧
1. 命名捕获组(ES2018+)
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; const match = '2024-01-15'.match(dateRegex); console.log(match.groups.year); // 2024 console.log(match.groups.month); // 01 console.log(match.groups.day); // 15
2. Unicode属性转义(ES2018+)
// 匹配所有中文字符 const chineseRegex = /\p{Script=Han}+/u; console.log(chineseRegex.test('你好世界')); // true // 匹配所有表情符号 const emojiRegex = /\p{Emoji}+/u; console.log(emojiRegex.test('😀😃😄')); // true
3. 替换回调函数
const text = 'hello world, hello javascript'; const result = text.replace(/hello/g, (match, offset) => { console.log(`Found "${match}" at position ${offset}`); return 'hi'; }); console.log(result); // Found "hello" at position 0 // Found "hello" at position 13 // hi world, hi javascript
性能优化
1. 避免回溯灾难
// ❌ 糟糕:可能导致灾难性回溯 const badRegex = /(a+)+b/; // 对于没有 b 的长字符串 'aaaa...aaa',会尝试所有可能的组合 // ✅ 优化:使用原子组或占有量词 const goodRegex = /a+b/;
2. 使用非捕获组
// ❌ 不必要的捕获 const regex1 = /(\d{4})-(\d{2})-(\d{2})/; // ✅ 只捕获需要的部分 const regex2 = /\d{4}-(\d{2})-\d{2}/; // 只捕获月份
3. 预编译正则表达式
// ❌ 每次都重新编译 function validate(text) { return /\d{3}-\d{4}/.test(text); } // ✅ 预编译,性能更好 const phoneRegex = /\d{3}-\d{4}/; function validate(text) { return phoneRegex.test(text); }
常见陷阱
1. 忘记转义特殊字符
// ❌ 错误:. 会匹配任意字符 const wrong = /www.example.com/; console.log(wrong.test('wwwXexample.com')); // true(不是我们想要的) // ✅ 正确:转义 . const correct = /www\.example\.com/; console.log(correct.test('wwwXexample.com')); // false
2. 全局匹配的状态问题
const regex = /test/g; console.log(regex.test('test')); // true console.log(regex.test('test')); // false(lastIndex 已经改变) // 解决方案:重置 lastIndex 或每次创建新的 RegExp regex.lastIndex = 0; console.log(regex.test('test')); // true
3. 多行模式的误解
const text = `line1 line2 line3`; // 默认情况下,^ 和 $ 只匹配整个字符串的开始和结束 const regex1 = /^line/; console.log(text.match(regex1)); // ['line'] (只匹配第一行) // 使用 m 标志,^ 和 $ 匹配每一行的开始和结束 const regex2 = /^line/gm; console.log(text.match(regex2)); // ['line', 'line', 'line']
总结
正则表达式是一个强大但复杂的工具。掌握它需要:
- 理解基础语法:元字符、量词、字符类等
- 大量练习:通过实际案例加深理解
- 注意性能:避免回溯灾难,优化复杂表达式
- 善用工具:使用 ToolsForge 正则测试工具 在线测试和调试
推荐资源
- MDN 正则表达式文档
- Regex101 - 在线正则测试工具
- RegExr - 可视化正则表达式学习工具
相关工具
关键词: 正则表达式, RegEx, 模式匹配, 文本处理, JavaScript正则, 数据验证
更新时间: 2026-01-04
相关阅读
开发教程
正则表达式实战指南:从入门到精通
深入学习正则表达式的语法、技巧和实际应用,通过大量实例掌握这一强大的文本处理工具
2026-01-01
其他
TypeScript 类型体操:什么时候值,什么时候在炫技
务实的 TypeScript 高级类型指南 — mapped types、conditional types、template literal types 真正能给你什么,什么时候用,什么时候应该退回到朴素代码。
2026-05-21
其他
Kubernetes 资源 requests / limits 实战:不会把生产搞挂的设法
怎么在生产里实际设 Kubernetes CPU 与内存的 requests/limits — QoS 类、CPU 节流、OOM kill、那些害公司钱的差别,以及好使的模式。
2026-05-18