上手:真实场景与边界
常用模式速查
怎么用这一篇
下面每条模式都由闸门实跑验证过,可以直接抄。但抄之前请先读「局限」那一段—— 这一篇真正的价值不在模式本身(那些网上到处都是),而在于把每条的边界写出来了。
🚨 网上流传的「万能正则」之所以害人,不是因为它们写错了, 而是因为没人告诉你它们什么时候不适用。
一条通用判据放在最前面:
先问「这个格式有标准吗」。 有标准(URL、日期、JSON、CSV)就用解析器; 正则适合的是没有标准、但形式固定的那一类。
数字
整数(可带负号)
⚠️ 不接受 +1、不接受千分位逗号,不做范围检查——
99999999999999999999 也会通过。范围永远交给数值比较,别往正则里塞。
正整数(不许前导零)
[1-9] 开头就挡掉了 007 和 0。如果 0 本身合法,写成 ^(?:0|[1-9]\d*)$。
小数
⚠️ 不接受 .5、3.,也不接受科学计数法 1e3。
要那些就别用正则了——Number(s) 配 Number.isFinite() 更准也更短。
常见格式
中国大陆手机号
🚨 这是本篇局限最大的一条:[3-9] 是当前的经验值,不是标准,号段会变。
它只能挡掉明显不像的输入。真要确认号码有效,只有发短信这一条路。
IPv4 地址
/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$/
⭐ 这是少数值得在正则里做范围校验的场景——四段结构固定,
每段的取值范围能用三条分支穷举完。常见的偷懒版 ^(?:\d{1,3}\.){3}\d{1,3}$
会放行 256.1.1.1。
⚠️ 不管 IPv6,也不管前导零(01.1.1.1 会通过)。
十六进制颜色
/^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i
两支分别覆盖 #fff 和 #ffffff。⚠️ 不含带透明度的 #rgba / #rrggbbaa。
邮箱(宽松版)
/^[^\s@]+@[^\s@]+\.[^\s@]+$/
这一条在实战那篇展开讲过,
结论是:别追求严格。三段、各段不含空格和 @、中间有个点,
投入产出比最高;剩下的交给验证邮件。
文本处理
去掉首尾空白
📌 实际项目里直接用 String.prototype.trim()。
收录它只是为了演示「| 配上锚点」的用法——不加锚点的 /\s+/g
会把中间的空白也删掉。
压缩连续空白为一个空格
/\s+/g → ' '
这个没有内置方法,是正则的主场。注意 \s 覆盖 U+00A0、全角空格
(见调试那篇),处理外部文本时正需要这种宽容。
千分位
/\B(?=(\d{3})+$)/g → ','
原理见先行与后行那篇——整条模式匹配的是零宽位置, 所以「替换」等于插入。⚠️ 只适用于纯数字串,带小数点的要先切开。
提取连续中文
⚠️ [一-龥] 是 U+4E00–U+9FA5 常用区,不含扩展区生僻字,也不含日文假名——
中日混排的文本上会出意外。要严谨用 \p{Script=Han} 配 u。
一张「别用正则」的对照表
抄之前先看看这里有没有更好的选择:
| 需求 | 别写正则,用 |
|---|---|
| 判断包含某个子串 | String.includes() |
| 按固定分隔符切分 | String.split() |
| 去首尾空白 | String.trim() |
| 大小写转换后比较 | toLowerCase() |
| 解析 URL 的各个部分 | new URL() |
| 解析日期 | Date / 日期库 |
| 解析 HTML、JSON、CSV | 对应的解析器 |
| 判断数字有效性与范围 | Number() + 比较 |
👉 判据很简单:如果标准库里有一个函数名字就叫这件事,就用它。 正则的价值在于处理那些「没有专门函数、但形式有规律」的文本。
抄走之前
三个动作,加起来不到一分钟:
- 读一遍这条的「⚠️ 局限」 —— 它是否覆盖了你的输入?
- 拿一个「应该被拒绝」的输入试一次 —— 大多数问题在这一步暴露
- 如果模式来自别处而不是这里,先看调试那篇
的第一步:
console.log(re.source, re.flags),确认你跑的是你以为的那条
全书到此结束
按阅读顺序回头看一遍:
10 分钟写出第一条 → 引擎在干什么 → 字符与字符类 → 量词与贪婪懒惰 → 锚点与零宽 → 分组与反向引用 → 选择分支与优先级 → 先行与后行 → 修饰符 → 实战三件事 → 不工作怎么查 → 什么时候不该用 → 方言差异 → 让同事读懂 → 一个需求走完全程 → 这一篇。
如果只带走一句话,建议是这句:
正则擅长「找到它」,不擅长「决定它对不对」。
剩下的都可以查——包括这一页。
本篇示例
下面每一条都由 npm run test:regex 在每次构建前实跑验证, 结果是现算的,不是抄进数据里的副本。正文里的代码块只用来演示匹配过程和写法对照,不进闸门; 凡是「这个模式配这个输入得到这个结果」的断言,只存在于这里。
整数(可带负号)
- 调用
/^-?\d+$/.test("-42")- 结果
true- 换成
/^\d+$/ false
⚠️ 局限:不接受
+1、不接受千分位逗号、不做范围检查(99999999999999999999也过)。范围要用数值比较。正整数(不许前导零)
- 调用
/^[1-9]\d*$/.test("007")- 结果
false- 换成
/^\d+$/ true
[1-9]开头挡掉007和0。⚠️ 如果 0 本身该算合法,写成^(?:0|[1-9]\d*)$。小数(整数部分必填,小数部分可选)
- 调用
/^-?\d+(?:\.\d+)?$/.test("42")- 结果
true- 换成
/^-?\d+\.\d+$/ false
⚠️ 局限:不接受
.5和3.,也不接受科学计数法1e3。要那些就别用正则,用Number()配isFinite。中国大陆手机号
- 调用
/^1[3-9]\d{9}$/.test("12800138000")- 结果
false- 换成
/^1\d{10}$/ true
🚨 局限最大的一条:号段会变。
[3-9]是当前的经验值,不是标准。真要确认号码有效,只能发短信 —— 同[邮箱那条](/regex/practice/validate-extract-replace/)。IPv4 地址(含 0–255 范围校验)
- 调用
/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$/.test("256.1.1.1")- 结果
false- 换成
/^(?:\d{1,3}\.){3}\d{1,3}$/ true
⭐ 这是少数值得在正则里做范围校验的场景(四段结构固定)。⚠️ 但它不管 IPv6、不管前导零
01.1.1.1。十六进制颜色(#fff 或 #ffffff)
- 调用
/^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test("#ffff")- 结果
false- 换成
/^#[0-9a-f]+$/i true
两支分别覆盖 3 位和 6 位缩写。⚠️ 不含
#rgba/#rrggbbaa这两种带透明度的新写法。去掉首尾空白(保留中间)
- 调用
" a b ".replace(/^\s+|\s+$/g, "")- 结果
"a b"- 换成
/\s+/g "ab"
📌 实际项目里直接用
String.prototype.trim()—— 收录它是为了演示|加锚点的组合用法。能用内置方法就别写正则。提取连续的中文
- 调用
[..."去北京玩 abc".matchAll(/[一-龥]+/g)]- 结果
["去北京玩"]- 换成
/\w+/g ["abc"]
⚠️
[一-龥]是 U+4E00–U+9FA5 常用区,不含扩展区生僻字、不含日文假名。更严谨用\p{Script=Han}配u(见[修饰符那篇](/regex/practice/flags/))。