正则表达式完全指南:元字符/量词/分组/断言
Introduction
正则表达式(Regular Expression)是处理字符串的强大工具,广泛用于表单验证、文本搜索替换、数据提取等场景。无论是 JavaScript、Python 还是 PHP,几乎所有编程语言都支持正则。本指南系统讲解正则的核心概念——元字符、量词、分组、断言,助你从零到精通正则表达式。
基础语法
1. 元字符(Metacharacters)
. 匹配除换行外任意字符
\d 匹配数字(0-9)
\D 匹配非数字
\w 匹配字母、数字、下划线([A-Za-z0-9_])
\W 匹配非\w
\s 匹配空白字符(空格、制表、换行)
\S 匹配非空白字符
\b 匹配单词边界
^ 匹配字符串开头
$ 匹配字符串结尾
\. \* \+ \? \\ 转义元字符
[abc] 匹配 a、b 或 c
[^abc] 匹配除了 a、b、c 以外的字符
[a-z] 匹配小写字母
2. 量词(Quantifiers)
* 匹配0次或多次(等价于 {0,})
+ 匹配1次或多次(等价于 {1,})
? 匹配0次或1次(等价于 {0,1})
{n} 恰好 n 次
{n,} 至少 n 次
{n,m} n 到 m 次
.* 贪婪:匹配尽可能多
.*? 非贪婪:匹配尽可能少
3. 分组与捕获
(\d{4})-(\d{2})-(\d{2}) 捕获年、月、日三组
(?:\d{4}) 非捕获组
(?<year>\d{4}) 命名捕获组(ES2018)
(['"])(.*?)\1 反向引用
cat|dog 匹配 'cat' 或 'dog'
4. 断言(Lookahead / Lookbehind)
(?=...) 正向先行断言 — "后面跟着..."
(?!...) 负向先行断言 — "后面不跟..."
(?<=...) 正向后行断言 — "前面是..."(ES2018)
(?<!...) 负向后行断言 — "前面不是..."
完整代码示例
const str = 手机:13812345678 邮箱:zhangsan@example.com 网站:https://www.example.com IP地址:192.168.1.100 日期:2024-01-15 人民币:¥1000.00 美元:$599.00;
const mobileRegex = /^1[3-9]\d{9}$/;
const mobiles = str.match(/1[3-9]\d{9}/g);
console.log('手机号:', mobiles);
const dateRegex = /(\d{4})-(\d{2})-(\d{2})/;
const dateMatch = str.match(dateRegex);
console.log('日期:', dateMatch?.[0], '年:', dateMatch?.[1], '月:', dateMatch?.[2], '日:', dateMatch?.[3]);
const emailRegex = /[\w.-]+@[\w.-]+\.\w+/g;
console.log('邮箱:', str.match(emailRegex));
const urlRegex = /https?:\/\/[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=.]*/g;
console.log('网址:', str.match(urlRegex));
const ipRegex = /\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b/g;
console.log('IP地址:', str.match(ipRegex));
const rmbRegex = /¥(\d+(?:\.\d{2})?)/;
const usdRegex = /\$(\d+(?:\.\d{2})?)/;
console.log('人民币:', str.match(rmbRegex)?.[0], '金额:', str.match(rmbRegex)?.[1]);
console.log('美元:', str.match(usdRegex)?.[0], '金额:', str.match(usdRegex)?.[1]);
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d]{8,}$/;
console.log('密码验证:', passwordRegex.test('Pass1234'));
const htmlRegex = /<[^>]+>/g;
const cleanText = str.replace(htmlRegex, '');
console.log('去除HTML标签后:', cleanText);
const toSnakeCase = (str) => str.replace(/[A-Z]/g, letter => _${letter.toLowerCase()});
console.log('驼峰转下划线:', toSnakeCase('userNameOfTheApplication'));
const highlight = (text, keyword) => text.replace(new RegExp((${keyword}), 'g'), '$1');
console.log('高亮关键词:', highlight('正则表达式很强大,正则表达式很有用', '正则'));
const quotedRegex = /(['"])(.*?)\1/g;
console.log('引号内容:', str.match(quotedRegex));
const namedDateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const namedMatch = str.match(namedDateRegex);
console.log('命名捕获:', namedMatch?.groups);
常见问题
- 贪婪和非贪婪(惰性)如何选择? 默认是贪婪(尽可能多匹配),当需要精确匹配时应使用非贪婪(加
?)。例如匹配 HTML 标签内容,应用<div>.*?</div>而非<div>.*</div>。 - 为什么有的正则匹配不到中文? 字符类
[a-z]只匹配 ASCII 字符,匹配中文要用[\u4e00-\u9fa5]或 Unicode 属性。 - 正则表达式性能如何优化? 避免使用
.*这种贪婪模式,改用非贪婪或明确字符集;避免回溯,使用锚点(^、$)限定范围。 - lookbehind(后行断言)浏览器支持情况? Chrome 62+、Firefox 78+、Safari 16.4+ 已支持。