打底:它到底在做什么
10 分钟写出你的第一条正则
这一篇不解释「为什么」
后面十一篇都在解释为什么。这一篇只有一个目的:让你在十分钟内写出一条能用的正则, 并且知道它为什么能用。
任务:校验一个 11 位手机号(以 1 开头,后面十位数字)。
你需要认识的东西一共四个,一个都不多。
第 1 步:先写死
正则里,普通字符就匹配它自己。所以这已经是一条合法的正则了:
/13800138000/
它能匹配 13800138000。问题也很明显——它对别的号码一律说不。
但这一步不是白写的:它确立了一件事,正则是「一个模板」,从左到右逐个字符比对。 接下来要做的只是把「会变的那部分」换成能代表一类字符的写法。
第 2 步:把会变的部分换掉
手机号里变的是后面那十位数字。「一个数字」写作 \d:
/1\d\d\d\d\d\d\d\d\d\d/
能用,但难看。「重复十次」写作 {10}:
/1\d{10}/
到这里你已经认识三个东西了:普通字符、\d、{n}。
^1\d{10}$ 匹配 13900139000,而少写一位的 ^1\d{9}$ 不匹配——
说明 {10} 是精确的十次,不是「大约十位」。
(先别管那两个 ^ $,下一步就是它们。)
🚨 第 3 步:说明「整串就这么长」
这是最关键的一步,也是新手最常漏的一步。
/1\d{10}/ 有个要命的问题:它问的是「这串里含不含一个 11 位手机号」,
而不是「这串是不是一个手机号」。于是:
/1\d{10}/ 会放行 '13800138000abc'。
在一个注册表单里,这意味着用户能提交任何以手机号开头的垃圾。
解决办法是在两端各加一个符号:
^ 表示「字符串从这里开始」
$ 表示「字符串到这里结束」
/^1\d{10}$/ // ← 这就是最终答案
加上之后,'13800138000abc' 就过不了了:^ 要求从头开始、$ 要求到此为止,
中间只允许 1 加十位数字,多一个字符都不行。
👉 把这条记成习惯:写校验类正则,先写 ^ 和 $,再往中间填东西。
它能挡掉你未来一半以上的 bug。
跑一遍
打开浏览器控制台(F12),粘贴:
const PHONE = /^1\d{10}$/;
PHONE.test('13800138000') // 你觉得是什么?
PHONE.test('13800138000abc')
PHONE.test('1380013800')
⚠️ 真的去跑一遍。 这一步不能用「我看懂了」代替—— 正则是一种写的技能,而写的技能只能靠手熟。
你现在会的四个东西
| 写法 | 意思 |
|---|---|
abc |
就是这三个字符 |
\d |
任意一个数字(0-9) |
{10} |
前面那个东西重复 10 次 |
^ $ |
字符串的开头 / 结尾 |
就靠这四个,你已经能写出不少实用的校验了——下面的练习就是。
一个现在就该知道的坑
\d{10} 里的 {10} 管的是它左边紧挨着的那一个东西,也就是 \d,
不是「前面那一串」。所以:
1\d{10} 1,然后十个数字 ← 对
(1\d){10} 「1 加一个数字」重复十次 ← 完全是另一回事
这条规则后面会反复用到(量词只管紧挨着的一个元素),现在只要记住
「重复」永远只作用于紧挨着的那一个就够了。
接下来读什么
你现在能写简单的校验了,但还不知道它为什么这样工作—— 于是遇到稍微复杂一点的情况就会卡住,比如:
- 为什么我的正则匹配到了一大坨我不想要的东西?
- 为什么同样一条正则,在别人的代码里能跑、粘到我这儿就不行?
《正则引擎在干什么》 回答第一类问题。 它讲的模型很小——引擎只做三件事——但后面所有的「坑」都是那三件事的推论。 先花十分钟把它读完,比先背五十个符号有用得多。
⚠️ 但在那之前,先把下面的练习做了。三道题,都只用到这一篇教的四个东西。
本篇示例
下面每一条都由 npm run test:regex 在每次构建前实跑验证, 结果是现算的,不是抄进数据里的副本。正文里的代码块只用来演示匹配过程和写法对照,不进闸门; 凡是「这个模式配这个输入得到这个结果」的断言,只存在于这里。
写死的模式只匹配它自己
- 调用
/13800138000/.test("13900139000")- 结果
false- 换成
/1\d{10}/ true
这就是「写死」的全部问题:它对别的手机号一律说不。
\d{10}= 十个数字- 调用
/^1\d{10}$/.test("13900139000")- 结果
true- 换成
/^1\d{9}$/ false
\d是「一个数字」,{10}是「重复十次」。手机号首位固定是 1,所以写成1加十个\d。🚨 不加
^ $,后面跟一坨垃圾也能过- 调用
/^1\d{10}$/.test("13800138000abc")- 结果
false- 换成
/1\d{10}/ true
⚠️ 校验类正则必须两端锚定。这是全书最常见的一个坑,也是你现在就该养成的习惯。
练习
先自己写,再看答案。读懂和写得出是两件事,而这一节练的是后者。每道题的参考答案都由 npm run test:exercises-regex 实跑验证: 答案必须通过全部用例,「常见错解」必须至少被一条用例抓住, 而且 /.*/ 这类万能写法必须过不了 —— 否则这道题就没有区分度。
校验一个六位数字的短信验证码:整串必须正好是 6 个数字
用 re.test(输入) 判断
输入 期望 "123456"应匹配 "12345"不应匹配 "1234567"不应匹配 "abc123"不应匹配 "123 456"不应匹配 提示
先写「六个数字」,再想「怎么说明整串就这么长」。
参考答案
/^\d{6}$/少了
^和$,问的就变成「串里含不含六位数字」—— 七位数字的串里当然含。校验类正则必须两端锚定。常见错解
/\d{6}/—— 它在"1234567"这条上就错了。校验 QQ 号:5 到 11 位数字
用 re.test(输入) 判断
输入 期望 "12345"应匹配 "12345678901"应匹配 "1234"不应匹配 "123456789012"不应匹配 "1234a"不应匹配 提示
{n,m}表示「n 到 m 次」。注意逗号后面不能有空格。参考答案
/^\d{5,11}$/半锚定是最危险的写法 —— 它对合法输入全对,只在「前缀合法、后面还有东西」时失效,而那恰恰是被人构造出来的那一类输入。
常见错解
/^\d{5,11}/—— 它在"123456789012"这条上就错了。校验日期格式
2026-09-11:四位年、两位月、两位日,中间用连字符用 re.test(输入) 判断
输入 期望 "2026-09-11"应匹配 "2026-9-11"不应匹配 "20260911"不应匹配 "2026-09-11x"不应匹配 参考答案
/^\d{4}-\d{2}-\d{2}$/连字符是普通字符,直接写即可。⚠️ 但这条只管格式:
2026-13-45也会通过 —— 值合不合法得交给Date判,见[实战那篇](/regex/practice/validate-extract-replace/)。常见错解
/^\d{4}-\d{2}-\d{2}/—— 它在"2026-09-11x"这条上就错了。