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 匹配 abca1c
\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 支持
`ab``(catdog)s?`
(?=...) / (?!...)正向 / 负向先行断言\d(?=px) 匹配 2px 里的 2
(?<=...) / (?<!...)正向 / 负向后行断言见引擎差异

常用验证表达式(直接可抄)

目标表达式说明
邮箱(简化)[\w.+-]+@[\w-]+\.[\w.-]+只做格式粗校验,生产用库
URLhttps?://[\w.-]+(/[\w./?%&=+-]*)?含路径与查询参数
日期 yyyy-mm-dd\d{4}-\d{2}-\d{2}格式校验;语义校验另写
IPv4(严格 0-255)`((25[0-5]2[0-4]\d1\d\d[1-9]?\d)\.){3}(25[0-5]2[0-4]\d1\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}\b3 位简写用 {3}
24 小时时间 HH:MM`([01]\d2[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'

高频坑排查

  1. grep -E\d 不生效:POSIX ERE 没有 \d,用 [0-9],或改用 grep -P(PCRE)。
  2. 贪婪匹配吞掉整行. 尽量多匹配,<a>(.)</a> 会匹配到最后一个 </a>;用 .*?
  3. 匹配不到中文\w 不含中文;用 [\u4e00-\u9fa5](Python/JS)或 PCRE 开 UCP 后用 \p{Script=Han}
  4. shell 转义:双引号里 $ 会被 shell 展开,"$" 要写成 \$;正则整体用单引号最稳。
  5. 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)。

来源

最后更新:2026-08-07