TL;DR
正则匹配三步走:先锚定位置(^ $ \b),再选字符类(\d \w [0-9]),最后控制次数(+ {n,m})。最容易踩的坑:grep -E 不认 \d(POSIX ERE 要用 [0-9],或改用 grep -P);JS 后行断言 (?<=...) 从 Safari 16.4(2023-03 起全平台)才可用;贪婪匹配会吞掉一整行,用 ? 懒惰匹配。
基础语法速查
| 语法 | 含义 | 例子 | ||
|---|---|---|---|---|
. | 任意字符(默认不含换行) | a.c 匹配 abc、a1c | ||
\d / \w / \s | 数字 / 单词字符 / 空白 | 注意引擎差异(见下表) | ||
[abc] / [^abc] | 字符集 / 取反 | [0-9]{3} 三位数字 | ||
^ / $ | 行首 / 行尾 | ^https 只匹配行首 | ||
\b | 单词边界 | \bcat\b 不匹配 category | ||
* / + / ? | 0 次以上 / 1 次以上 / 0 或 1 次 | colou?r 匹配 color/colour | ||
*? / +? | 懒惰(非贪婪)量词 | .*? 尽量少匹配 | ||
{n,m} / {n,} / {n} | n 到 m 次 / 至少 n 次 / 恰好 n 次 | \d{4}-\d{2}-\d{2} | ||
(...) / (?:...) | 捕获组 / 非捕获组 | 捕获组可反向引用 | ||
(?<name>...) | 命名捕获组 | Python/PCRE/JS 支持 | ||
| `a | b` | 或 | `(cat | dog)s?` |
(?=...) / (?!...) | 正向 / 负向先行断言 | \d(?=px) 匹配 2px 里的 2 | ||
(?<=...) / (?<!...) | 正向 / 负向后行断言 | 见引擎差异 |
常用验证表达式(直接可抄)
| 目标 | 表达式 | 说明 | ||||||
|---|---|---|---|---|---|---|---|---|
| 邮箱(简化) | [\w.+-]+@[\w-]+\.[\w.-]+ | 只做格式粗校验,生产用库 | ||||||
| URL | https?://[\w.-]+(/[\w./?%&=+-]*)? | 含路径与查询参数 | ||||||
| 日期 yyyy-mm-dd | \d{4}-\d{2}-\d{2} | 格式校验;语义校验另写 | ||||||
| IPv4(严格 0-255) | `((25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d)\.){3}(25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d)` | 简版 (\d{1,3}\.){3}\d{1,3} 会放过 999 |
| 中国大陆手机号 | 1[3-9]\d{9} | 11 位,第二位 3-9 | ||||||
| 中文 | [\u4e00-\u9fa5] | Unicode CJK 基本区 | ||||||
| 空白行 | ^\s*$ | 批量清理空行 | ||||||
| 十六进制颜色 | #[0-9a-fA-F]{6}\b | 3 位简写用 {3} | ||||||
| 24 小时时间 HH:MM | `([01]\d | 2[0-3]):[0-5]\d` | 23:59 合法,24:00 不合法 |
引擎差异(最容易写错的地方)
| 特性 | PCRE(grep -P / PHP / 多数语言) | ERE(grep -E / sed -E) | JavaScript |
|---|---|---|---|
\d \w \s 简写 | 支持 | 不支持,用 [0-9] 等 | 支持 |
| 命名分组 | (?<name>) | 不支持 | (?<name>) |
后行断言 (?<=) | 支持 | 不支持 | Safari 16.4+(2023-03 全平台) |
懒惰量词 *? | 支持 | GNU 支持(POSIX 未定义) | 支持 |
| 替换引用 | $1 或 \1 | \1 | $1 |
命令行与代码示例
# 统计 access.log 中每个 IPv4 的出现次数(grep -E 用 [0-9] 而不是 \d)
grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log | sort | uniq -c | sort -rn | head
# 统计 5xx 错误数
grep -cE 'HTTP/[0-9.]+" [5][0-9]{2}' app.log
# sed 把 2026-08-07 改成 2026/08/07(-E 即 ERE,分组引用用 \1)
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\1\/\2\/\3/g' data.txt
# 提取非空行
grep -E '.+' file.txt
# Python re:提取所有中文
import re
re.findall(r'[\u4e00-\u9fa5]+', 'hello 世界') # ['世界']
# 贪婪 vs 懒惰:同一行有两个 <a> 标签
re.findall(r'<a>(.*?)</a>', '<a>1</a><a>2</a>') # ['1','2'](.*? 懒惰)
re.findall(r'<a>(.*)</a>', '<a>1</a><a>2</a>') # ['1</a><a>2'](.* 贪婪)
# 命名分组
m = re.search(r'(?P<year>\d{4})-(?P<month>\d{2})', '2026-08-07')
m.group('year') # '2026'
// JS 后行断言提取价格(Safari 16.4+ 可用)
"价格 299 元".match(/(?<=价格 )\d+/); // ['299']
// 兼容旧 Safari 的写法:捕获组
"价格 299 元".match(/价格 (\d+)/)[1]; // '299'
高频坑排查
grep -E里\d不生效:POSIX ERE 没有\d,用[0-9],或改用grep -P(PCRE)。- 贪婪匹配吞掉整行:
.尽量多匹配,<a>(.)</a>会匹配到最后一个</a>;用.*?。 - 匹配不到中文:
\w不含中文;用[\u4e00-\u9fa5](Python/JS)或 PCRE 开 UCP 后用\p{Script=Han}。 - shell 转义:双引号里
$会被 shell 展开,"$"要写成\$;正则整体用单引号最稳。 - JS 环视在旧 Safari 报错:后行断言 Safari 16.4 前不支持,用捕获组改写(见上)。
常见问题
grep 里 \d 为什么匹配不到数字?
grep -E(POSIX ERE)不支持 \d 简写,要写 [0-9];GNU grep 用 grep -P(PCRE)才认 \d。例:grep -Eo '[0-9]{4}' 而不是 grep -Eo '\d{4}'。
怎么提取两个标签之间的内容?
用非贪婪 .?:<a>(.?)</a>。贪婪的 .* 会匹配到最后一个闭合标签,把中间所有内容吞掉;一行有多个标签时尤其明显。
正则怎么匹配中文?
常用 [\u4e00-\u9fa5](Unicode CJK 基本区,Python/JS 直接支持);PCRE 可开 UCP 后用 \p{Script=Han}。注意 \w 只含 ASCII 单词字符,不含中文。
JavaScript 的环视(lookbehind)所有浏览器都支持吗?
后行断言 (?<=...) 自 ES2018 起支持,Safari 16.4+(约 2023 年 3 月起全平台可用)。要兼容旧 Safari 就用捕获组改写:/价格 (\d+)/ 替代 /(?<=价格 )\d+/。
验证邮箱用正则够吗?
不够。正则只能做格式粗校验([\w.+-]+@[\w-]+\.[\w.-]+),无法验证域名是否存在、MX 记录是否有效;生产环境建议用专门库(Python email-validator、JS validator.js)。
来源
- MDN Regular Expressions(lookbehind 兼容性,2023-03 全平台可用)
- regular-expressions.info
- GNU grep 手册(ERE 与 PCRE 差异)
- caniuse.com/js-regexp-lookbehind(Safari 16.4+)
- 核实日期:2026-08-07