上手:真实场景与边界

常用模式速查

怎么用这一篇

下面每条模式都由闸门实跑验证过,可以直接抄。但抄之前请先读「局限」那一段—— 这一篇真正的价值不在模式本身(那些网上到处都是),而在于把每条的边界写出来了。

🚨 网上流传的「万能正则」之所以害人,不是因为它们写错了, 而是因为没人告诉你它们什么时候不适用。

一条通用判据放在最前面:

先问「这个格式有标准吗」。 有标准(URL、日期、JSON、CSV)就用解析器; 正则适合的是没有标准、但形式固定的那一类。

数字

整数(可带负号)

/^-?\d+$/

⚠️ 不接受 +1、不接受千分位逗号,不做范围检查—— 99999999999999999999 也会通过。范围永远交给数值比较,别往正则里塞。

正整数(不许前导零)

/^[1-9]\d*$/

[1-9] 开头就挡掉了 007 和 0。如果 0 本身合法,写成 ^(?:0|[1-9]\d*)$。

小数

/^-?\d+(?:\.\d+)?$/

⚠️ 不接受 .5、3.,也不接受科学计数法 1e3。 要那些就别用正则了——Number(s) 配 Number.isFinite() 更准也更短。

常见格式

中国大陆手机号

/^1[3-9]\d{9}$/

🚨 这是本篇局限最大的一条:[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@]+$/

这一条在实战那篇展开讲过, 结论是:别追求严格。三段、各段不含空格和 @、中间有个点, 投入产出比最高;剩下的交给验证邮件。

文本处理

去掉首尾空白

/^\s+|\s+$/g

📌 实际项目里直接用 String.prototype.trim()。 收录它只是为了演示「| 配上锚点」的用法——不加锚点的 /\s+/g 会把中间的空白也删掉。

压缩连续空白为一个空格

/\s+/g → ' '

这个没有内置方法,是正则的主场。注意 \s 覆盖 U+00A0、全角空格 (见调试那篇),处理外部文本时正需要这种宽容。

千分位

/\B(?=(\d{3})+$)/g → ','

原理见先行与后行那篇——整条模式匹配的是零宽位置, 所以「替换」等于插入。⚠️ 只适用于纯数字串,带小数点的要先切开。

提取连续中文

/[一-龥]+/g

⚠️ [一-龥] 是 U+4E00–U+9FA5 常用区,不含扩展区生僻字,也不含日文假名—— 中日混排的文本上会出意外。要严谨用 \p{Script=Han} 配 u。

一张「别用正则」的对照表

抄之前先看看这里有没有更好的选择:

需求 别写正则,用
判断包含某个子串 String.includes()
按固定分隔符切分 String.split()
去首尾空白 String.trim()
大小写转换后比较 toLowerCase()
解析 URL 的各个部分 new URL()
解析日期 Date / 日期库
解析 HTML、JSON、CSV 对应的解析器
判断数字有效性与范围 Number() + 比较

👉 判据很简单:如果标准库里有一个函数名字就叫这件事,就用它。 正则的价值在于处理那些「没有专门函数、但形式有规律」的文本。

抄走之前

三个动作,加起来不到一分钟:

  1. 读一遍这条的「⚠️ 局限」 —— 它是否覆盖了你的输入?
  2. 拿一个「应该被拒绝」的输入试一次 —— 大多数问题在这一步暴露
  3. 如果模式来自别处而不是这里,先看调试那篇 的第一步:console.log(re.source, re.flags),确认你跑的是你以为的那条

全书到此结束

按阅读顺序回头看一遍:

10 分钟写出第一条 → 引擎在干什么 → 字符与字符类 → 量词与贪婪懒惰 → 锚点与零宽 → 分组与反向引用 → 选择分支与优先级 → 先行与后行 → 修饰符 → 实战三件事 → 不工作怎么查 → 什么时候不该用 → 方言差异 → 让同事读懂 → 一个需求走完全程 → 这一篇。

如果只带走一句话,建议是这句:

正则擅长「找到它」,不擅长「决定它对不对」。

剩下的都可以查——包括这一页。

本篇示例

下面每一条都由 npm run test:regex 在每次构建前实跑验证, 结果是现算的,不是抄进数据里的副本。正文里的代码块只用来演示匹配过程和写法对照,不进闸门; 凡是「这个模式配这个输入得到这个结果」的断言,只存在于这里。

  1. 整数(可带负号)

    调用
    /^-?\d+$/.test("-42")
    结果
    true
    换成 /^\d+$/
    false

    ⚠️ 局限:不接受 +1、不接受千分位逗号、不做范围检查(99999999999999999999 也过)。范围要用数值比较。

  2. 正整数(不许前导零)

    调用
    /^[1-9]\d*$/.test("007")
    结果
    false
    换成 /^\d+$/
    true

    [1-9] 开头挡掉 007 和 0。⚠️ 如果 0 本身该算合法,写成 ^(?:0|[1-9]\d*)$。

  3. 小数(整数部分必填,小数部分可选)

    调用
    /^-?\d+(?:\.\d+)?$/.test("42")
    结果
    true
    换成 /^-?\d+\.\d+$/
    false

    ⚠️ 局限:不接受 .5 和 3.,也不接受科学计数法 1e3。要那些就别用正则,用 Number() 配 isFinite。

  4. 中国大陆手机号

    调用
    /^1[3-9]\d{9}$/.test("12800138000")
    结果
    false
    换成 /^1\d{10}$/
    true

    🚨 局限最大的一条:号段会变。[3-9] 是当前的经验值,不是标准。真要确认号码有效,只能发短信 —— 同[邮箱那条](/regex/practice/validate-extract-replace/)。

  5. 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。

  6. 十六进制颜色(#fff 或 #ffffff)

    调用
    /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test("#ffff")
    结果
    false
    换成 /^#[0-9a-f]+$/i
    true

    两支分别覆盖 3 位和 6 位缩写。⚠️ 不含 #rgba / #rrggbbaa 这两种带透明度的新写法。

  7. 去掉首尾空白(保留中间)

    调用
    " a b ".replace(/^\s+|\s+$/g, "")
    结果
    "a b"
    换成 /\s+/g
    "ab"

    📌 实际项目里直接用 String.prototype.trim() —— 收录它是为了演示 | 加锚点的组合用法。能用内置方法就别写正则。

  8. 提取连续的中文

    调用
    [..."去北京玩 abc".matchAll(/[一-龥]+/g)]
    结果
    ["去北京玩"]
    换成 /\w+/g
    ["abc"]

    ⚠️ [一-龥] 是 U+4E00–U+9FA5 常用区,不含扩展区生僻字、不含日文假名。更严谨用 \p{Script=Han} 配 u(见[修饰符那篇](/regex/practice/flags/))。