正则表达式速查表 - 常用语法、模式与实战模板大全

正则真正难的地方不是元字符本身,而是同一个需求常有几种等价写法,选错一种就会在长文本上悄悄多吞或少吞字符。抓两个标签之间的内容时,.* 和 .*? 在多数样例上输出完全一样,只有当一行里出现第二个同名标签时才会分叉; 与显式空格的差别,在含下划线的标识符上同样看不出来。这张表把字符类、锚点、量词、分组与捕获、转义与标志、实战示例、常见陷阱拆成七组,每条都写明它匹配的是一个字符、一个位置还是一段结构。什么时候该翻它:模式在小样本上验证通过、上了真实数据就大面积失配;或 grep 出来的行数明显多于预期——这两种几乎都出在量词和断言上,先查贪婪还是懒惰,再补 ^/$ 或词边界。与 grep -E 的区别在于此表只讲模式语法本身,不绑定任何搜索工具;与 split 的区别在于 split 只能按固定分隔符切,模式能处理变长结构。

参考手册·共 48 条命令·最后更新 2026-08-02
regex正则patternmatch

典型使用场景

面向经常需要写正则但总在量词与断言上翻车的开发者:把「贪婪还是懒惰」「该用前瞻还是后顾」「哪些元字符字面使用时必须转义」这三类高频翻车点集中讲清,再配上邮箱、URL、手机号、日期、密码五个可直接用的实战模式。目标是写完能自己判断对错,而不是照抄一个能跑但不知道为什么能跑的模式。

字符类 Character Classes 10

[abc]
匹配 a、b 或 c 中任一个
[^abc]
匹配除 a、b、c 外的任意字符
[a-z]
匹配 a 到 z 的任意小写字母
\d
数字,等价于 [0-9]
\D
非数字
\w
单词字符(字母、数字、下划线),等价于 [A-Za-z0-9_]
\W
非单词字符
\s
空白字符(空格、制表符、换行)
\S
非空白字符
.
除换行外的任意单个字符

锚点 Anchors 8

^
匹配字符串开头
$
匹配字符串结尾
\b
单词边界
\B
非单词边界
(?=...)
正向前瞻:后面必须是 ... 但不消费
(?!...)
负向前瞻:后面不能是 ...
(?<=...)
正向后顾:前面必须是 ...
(?<!...)
负向后顾:前面不能是 ...

量词 Quantifiers 8

*
0 次或多次
+
1 次或多次
?
0 次或 1 次
{n}
恰好 n 次
{n,}
至少 n 次
{n,m}
n 到 m 次
*?
懒惰匹配:尽量少匹配(默认贪婪)
+?
懒惰的 1 次或多次

分组与捕获 Groups 6

(abc)
捕获分组,可在结果中取第 1 组
(?:abc)
非捕获分组,只用于组合不捕获
(?<name>abc)
命名捕获分组
a|b
匹配 a 或 b
\1
反向引用第 1 个捕获组
\k<name>
反向引用命名分组

转义与标志 Flags 7

\
匹配反斜杠本身
\.
匹配字面点号(转义元字符)
/i
忽略大小写
/g
全局匹配(找出所有)
/m
多行模式,^ $ 匹配每行
/s
dotAll,让 . 也能匹配换行
/u
Unicode 模式

实战示例 Practical Patterns 5

[\w.+-]+@[\w-]+\.[\w.-]+
邮箱
https?://[\w./?=&#%-]+
URL(http/https)
1[3-9]\d{9}
中国大陆手机号
^\d{4}-\d{2}-\d{2}$
YYYY-MM-DD 日期
^[#\w-]{6,}$
至少 6 位密码(字母数字下划线连字符)

常见陷阱 FAQ 4

贪婪 vs 懒惰
<.*> 会吞掉整行;<.*?> 只匹配最短
点号不含换行
跨行匹配需加 /s 标志或用 [\s\S]
特殊字符要转义
元字符 . ^ $ * + ? ( ) [ ] { } | \ / 在字面使用时需前加 \ 转义
catastrophic backtracking
嵌套量词 (.+)+ 在长串上可能指数级回溯,避免

参数矩阵

参数作用示例
* vs + vs ?0 次或多次 / 1 次或多次 / 0 或 1 次a*b · a+b · a?b
{n,m}精确次数范围,比多次 * 更可控^\d{4}-\d{2}-\d{2}$
*? / +?懒惰匹配,尽量少匹配<.*?> 只匹配最短标签
^ / $锚定字符串开头 / 结尾^start
\b单词边界,避免匹配到词内部\bcat\b 不匹配 concatenate
(?=...)正向前瞻:后面必须是但不消费\d+(?= 元)
(?<=...)正向后顾:前面必须是但不消费(?<=\$)\d+
(?:...)非捕获分组,只组合不捕获(?:foo|bar)+
(?<name>...)命名捕获,结果可按名取(?<year>\d{4})-(?<month>\d{2})
/i / /g / /m / /s忽略大小写 / 全局 / 多行 / dotAllstr.match(/\d+/g)
[\w.+-]+@[\w-]+邮箱实战模式[\w.+-]+@[\w-]+\.[\w.-]+
1[3-9]\d{9}中国大陆手机号1[3-9]\d{9}

易错点与避坑指南

现象想匹配 HTML 标签内容,结果匹配到了整行文本。

原因<.*> 是贪婪匹配,会一直吃到行尾最后一个 >。

处置改成懒惰匹配 <.*?>,或用 [^>]* 排除 > 字符。

现象在长字符串上正则运行极慢甚至卡死。

原因嵌套量词(如 (.+)+)在匹配失败时产生指数级回溯(catastrophic backtracking)。

处置避免嵌套量词;改用 [^...] 排除或分两步匹配。

现象点号匹配不到多行内容,跨行的文本只匹配到第一行。

原因默认 . 不匹配换行符。

处置加 /s 标志,或用 [\s\S] 显式表示任意字符。

现象搜 "cat" 时把 concatenate 也匹配出来了。

原因模式没有单词边界约束。

处置用 \bcat\b 限定边界。

现象搜文件名里的点号时匹配到了任意字符。

原因. 是正则元字符,字面使用未转义。

处置写成 \. 转义,或用 grep -F / re.escape() 按字面匹配。

现象写好 ^ 和 $ 但多行文本里只在首尾生效。

原因缺少多行标志,^ $ 默认匹配整个字符串的首尾。

处置加 /m 标志使其匹配每一行的行首行尾。

排障路径

  1. 1确认实际匹配了什么而不是你以为的

    grep -oE "你的模式" file

    -o 只输出匹配到的片段;没有输出说明模式没命中,逐段简化再试。

  2. 2在文本里定位边界是否存在

    grep -nE "\bword\b" file

    边界问题最常见的解法就是加 \b,先验证再加进主模式。

  3. 3逐段验证复杂模式

    grep -oE "^(\d{4})-(\d{2})-(\d{2})$" file

    拆成小组分别验证,比一次性调长模式更容易定位是哪段出错。

  4. 4在 JS 里测试并查看捕获组

    node -e "console.log('2026-09-30'.match(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/))"

    match 返回数组与 groups,命名组便于后续取值。

  5. 5排查贪婪匹配吞掉整行

    grep -oE "<[^>]*>" file

    用 [^>] 代替 .* 是避免贪婪最直接的办法。

命令示例

用懒惰量词取标签内容,避免贪婪匹配吞掉整行

const re = /<(\w+)[^>]*>(.*?)<\/\1>/g const line = '<b>bold</b> and <i>italic</i>' console.log([...line.matchAll(re)].map(m => m[1] + ':' + m[2]))

(.*?) 配合 *? 只取到第一个闭合尖括号;若写成 <(.*)>,贪婪量词会把 </b> and <i>italic 全部当成内容。\1 反向引用保证只匹配同名闭合标签。

输出

[ 'b:bold', 'i:italic' ]

贪婪量词在长串上的回溯爆炸

// 危险写法:对长字符串可能指数级回溯 const bad = /^(\w+\s?)+$/.test('a'.repeat(30) + '!') // 安全写法:单层量词 + 线性匹配 const good = /^\w+(?:\s+\w+)*$/.test('a'.repeat(30) + '!')

嵌套量词 (\w+\s?)+ 的匹配路径呈指数增长,30 个字符就足以让单次匹配卡住数秒;改写为不嵌套的 \w+(?:\s+\w+)* 后回到线性时间。

输出

bad: false
good: false

字符类里的连字符必须放在首尾

// 错误:a-z 被当成范围 'z' 到 '8' const bad = /^[a-z-]+$/.test('-') // 正确:连字符放首尾或转义 const ok = /^[-a-z]+$/.test('-')

[a-z-] 里的 - 跟在范围之后会被解析成 z 到 8 的反向范围,纯连字符字符串直接校验失败。放首尾或写 \- 才是想要的语义。

输出

bad: false
ok: true

用前后瞻校验密码强度而不消费字符

const re = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$/ re.test('Passw0rd') re.test('password1')

三个正向前瞻分别要求「含数字/小写/大写」,(?=...) 只做检查不消耗字符,因此主量词 .{8,} 仍能校验完整长度。若改成先捕获再回溯,长密码上的性能会明显变差。

输出

true
false

命名捕获分组替代数字下标

const re = /(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})$/ const m = '2026-07-19'.match(re) m.groups.y + '/' + m.groups.m + '/' + m.groups.d

给分组起名后调整分组顺序不会让 m[1]、m[2] 静默错位——这类错误在日志解析里极难发现。\k<name> 可反向引用命名分组。

输出

'2026/07/19'

常见坑与注意事项

  • 贪婪量词与嵌套量词组合((.+)+、^(a|aa)+$)在长输入上会指数级回溯,几十个字符就能让服务线程卡死,上线前必须用最长真实输入压测。
  • 字符类里把 - 写在中间会被解释成范围:[a-z-] 实际含 z 到 8 的反向区间,纯连字符输入会校验失败;- 只能放首尾或转义。
  • GNU 与 BSD 正则不兼容:grep -E "\d" 在 BSD(macOS)上不成立,需写 [[:digit:]];\w、\s、\b 同样是 GNU 扩展。
  • 未转义的元字符(. * + ? ( ) [ ] { } | \ ^ $)在字面量场景会被当成语法,写密码/邮箱校验前不做 \ 转义会漏匹配甚至误匹配。
  • . 默认不匹配换行,跨行文本提取会静默少内容;需要跨行时加 s 标志或改用 [\s\S],而 ^ $ 逐行匹配必须加 m。
  • /g 正则带 lastIndex 状态,复用同一个正则对象做多次 test/exec 会得到交替的结果;改用 String.prototype.matchAll 或每次新建正则。

提示

  • 默认量词贪婪,加 ? 变懒惰(<.*?> 只匹配最短)。
  • 点号 . 不含换行,跨行匹配需 /s 标志或 [\s\S]。
  • 元字符 . ^ $ * + ? ( ) [ ] { } | \ / 字面使用需转义。
  • 避免嵌套量词 (.+)+,长串上可能指数级回溯。
  • 命名捕获组 (?<name>abc) 配 \k<name> 替换,比按括号序号写 \2 更不怕后续加分组。
  • 手机号只写 \d{11} 会从 13 位数字里截出一段,1[3-9]\d{9} 才能排除非法号段。
  • \b 是零宽断言不消耗字符,grep -nE "\bword\b" 中它只在词边界处成立,不会吃掉相邻字符。

常见问题

什么时候该把贪婪量词改成懒惰?两者差别到底在哪?

默认量词都是贪婪的,会尽可能多吞字符,这正是灾难的来源。经典对照:匹配 HTML 标签用 <.*> 会把同一行里两个标签之间的全部内容一起吃掉,而 <.*?> 只取最短的闭合,得到真正的单个标签。选懒惰的判据是「匹配内容后面还跟着别的字符约束」——比如 <(\w+?)>、>.*?</div>,一旦标签名后面必须是尖括号或结束标签,就加 ?。反过来,如果后面没有可靠的分界符,懒惰只会匹配到空串,贪婪反而正确。

正则里的 . 明明写了却匹配不到换行,该怎么改?

点号的定义就是「除换行外的任意单个字符」,所以 <div>.*</div> 在多行文本上只会匹配同一行内的内容。两种解法:其一是在模式上加 /s(dotAll)标志,让 . 也能吞换行;其二是把 . 换成 [\s\S],因为它同时包含空白与非空白字符,天然覆盖换行,且不需要额外标志。若只想匹配有限几行,用 /m 让 ^ 和 $ 锚定到每一行行首行尾,再配合惰性量词,别一上来就开 /s 把整个文件都扫进来。

嵌套量词 (.+)+ 会让程序卡死,该怎么发现并改写?

成因是回溯:外层量词尝试一种划分失败后要退回内层重试,嵌套结构让失败路径呈指数级增长。典型症状是短字符串毫秒返回,稍长的输入直接吃掉 CPU 满载几秒甚至超时。改写方向有三:把 (.+)+ 换成 (.+)——分组里本来就没有量词必要;把重复的内容收紧为 (.+?)+ 仍不安全时,改用 ([^x]+)+ 排除不可能出现的字符;或干脆绕开正则,把匹配拆成两次单次匹配(本表里的 (?=...) 正向前瞻也可用来做「校验存在但不消费」的预处理)。写完用一段明显超长的字符串做一次冒烟测试是必要的。

捕获分组、(?:...) 和命名分组该怎么选?

看你要不要从结果里取值。普通 (abc) 是捕获组,匹配结果里可以按序号取出,替换里还能用 \1 反向引用。(?:abc) 是非捕获分组,只做组合不占编号——当一组只是为了把量词或分支绑在一起(比如把两个引号字符用 | 串起来再套一层)时用它,可以省下编号、避免编号错位。命名组 (?<name>abc) 在结果多、序号难记时最清楚,取值和反向引用都能写 \k<name>。原则:只为取值而捕获,其余一律用 (?:),结果结构复杂再升级为命名组。

校验手机号、日期、邮箱这类格式,正则该怎么写才不出错?

照本表的实战示例写:邮箱用 [\w.+-]+@[\w-]+\.[\w.-]+,中国大陆手机号用 1[3-9]\d{9},日期用 ^\d{4}-\d{2}-\d{2}$,密码用 ^[#\w-]{6,}$。三个细节决定成败:一是加 ^ 和 $ 锚定整串,否则 1[3-9]\d{9} 会从长文本中截出 11 位片段误判通过;二是 \d 只覆盖 ASCII 数字,写 \d{4}-\d{2}-\d{2} 时要意识到它并不等价于「任意数字」,需要更宽松就把字符类写成 [0-9] 加 u 模式;三是字符类里 # - _ 不需转义,但 . 在字符类中已是字面量,别再写成 \.。

官方参考来源

下方为命令对应的官方权威文档,供你核对最新用法与深入查阅。

由 巧匠 维护

公开更新于 2026年8月2日,内容持续校对官方文档。

联系我们

命令或描述有误?提交反馈、商务合作或产品建议都可发送邮件给我们。

联系我们