讲解
这一章把前面学的零件组装成能直接用的模式。先说清一个原则:下面这些模式是「实用版」,覆盖日常业务校验和提取,但不追求 RFC 级完备——邮箱和 URL 的完整规范复杂到没有可维护的正则能 100% 覆盖,工程上的做法是正则做粗筛、配合业务逻辑兜底。
中国手机号的实用写法是 /^1[3-9]\d{9}$/:1 开头、第二位 3 到 9、共 11 位。邮箱的实用版是 /^[\w.+-]+@[\w-]+(.[\w-]+)+$/:本地部分允许字母数字点和加减下划线,@ 后面是至少一段点分域名。日期 /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/ 把月份限定在 01-12、日限定在 01-31——但注意它不知道 2 月没有 30 号,格式之外的合法性要交给 Date 二次校验。
校验之外,提取是正则的另一半战场:从日志、配置、文本里抓结构化数据。matchAll 配合捕获组能把 key=value 这类片段成批取出。URL 的简化校验用 /^https?://[\w.-]+(:\d+)?(/\S*)?$/:协议、主机、可选端口、可选路径。这些模式都值得收藏,但更重要的是理解每一段的含义,按自己的业务改。
示例
// 中国手机号:1 开头,第二位 3-9,共 11 位
const mobile = /^1[3-9]\d{9}$/;
console.assert(mobile.test('13812345678'));
console.assert(mobile.test('19900001111'));
console.assert(mobile.test('12345678901') === false); // 第二位不在 3-9
console.assert(mobile.test('1381234567') === false); // 少一位
// 邮箱(实用版,非 RFC 完备)
const email = /^[\w.+-]+@[\w-]+(\.[\w-]+)+$/;
console.assert(email.test('some.one+tag@example.com'));
console.assert(email.test('a@b.co'));
console.assert(email.test('a@b') === false); // 没有点后缀
console.assert(email.test('@example.com') === false); // 本地部分为空
// 日期 YYYY-MM-DD(格式级校验:月 01-12,日 01-31)
const date = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
console.assert(date.test('2026-08-09'));
console.assert(date.test('2026-12-31'));
console.assert(date.test('2026-13-01') === false); // 没有 13 月
console.assert(date.test('2026-08-32') === false); // 没有 32 日
console.assert(date.test('2026-8-9') === false); // 必须补零
// 注意:2026-02-30 能通过格式校验!真正的日期合法性要二次校验
console.assert(date.test('2026-02-30') === true); // 格式合法但日期不存在
// 可靠的做法:用 Date 构造后比对各字段(不存在的日期会被「进位」)
const isRealDate = (s) => {
if (!date.test(s)) return false;
const [y, m, d] = s.split('-').map(Number);
const dt = new Date(Date.UTC(y, m - 1, d));
return dt.getUTCFullYear() === y && dt.getUTCMonth() === m - 1 && dt.getUTCDate() === d;
};
console.assert(isRealDate('2026-08-09') === true);
console.assert(isRealDate('2026-02-30') === false); // 2 月 30 日被进位到 3 月,比对失败
console.assert(isRealDate('2023-02-29') === false); // 非闰年没有 2 月 29 日
// URL(http/https 简化版)
const url = /^https?:\/\/[\w.-]+(:\d+)?(\/\S*)?$/;
console.assert(url.test('https://docs.ohmygp.com/regex'));
console.assert(url.test('http://localhost:8080/api'));
console.assert(url.test('https://example.com'));
console.assert(url.test('ftp://example.com') === false); // 只允许 http/https
console.assert(url.test('example.com') === false); // 必须有协议头
// 提取:从日志里批量抓键值对
const log = '[2026-08-09 12:00:01] user=alice action=login ip=10.0.0.1';
const entries = [...log.matchAll(/(\w+)=(\S+)/g)].map((m) => [m[1], m[2]]);
console.assert(entries.length === 3);
console.assert(entries.find(([k]) => k === 'user')[1] === 'alice');
console.assert(entries.find(([k]) => k === 'ip')[1] === '10.0.0.1');
常见坑
- 追求「完美邮箱正则」:RFC 5322 完备版正则有上百个字符且无人能维护。实用版粗筛 + 发验证邮件确认,是业界通行做法。
- 以为格式校验能代替逻辑校验:正则看不出 2 月 30 日、不存在的手机号号段、已停用的域名后缀。格式过了还要业务层二次校验。
- 照抄号段规则:手机号第二位、身份证地址码这类规则会随时间调整,硬编码的正则需要跟着业务更新。
- 不理解就收藏:上面的每个模式都要能讲出每一段的含义再拿去用;遇到业务差异(比如允许 IP 形式的 URL)才知道改哪里。
小结
手机号、邮箱、日期、URL 的实用模式可以直接收藏,但格式校验不等于逻辑校验,提取场景用 matchAll 加捕获组。下一节系统讲 JavaScript 里的正则 API。