邮件地址是 本地部分@域名,每一段都有长度上限。
254 这个数不是「地址的天性」,是从「命令行长度上限」倒推出来的。
8.4.1 讲了邮件怎么走,这一节讲「地址」这个最容易被忽略的细节。 这里会把真实的正则跑一遍,看它拦得住什么、拦不住什么。
一、四个长度上限
本地部分 64 字节、域名 255 字节、整个地址 254 字节、每一级标签 63 字节。 这四个数各有各的来历,不是拍脑袋定的。 64 这个数来自 SMTP 的路径语法: 本地部分允许写成带引号的字符串,规范里给这个字段留了 64 字节。 255 来自 DNS——一个域名整体不能超过 255 字节,因为 DNS 报文里的名字字段就是这个宽度。 63 是 DNS 单个标签的上限:它来自「标签长度用一个字节表示,高两位留作压缩指针」, 所以剩下六位,最大 63。 一个协议的长度限制,常常是「另一个协议的字段宽度」的遗产。
二、254 是怎么倒推出来的
SMTP 命令里地址要写成 <a@b> 的形式,两侧尖括号占 2 字节。
而规范给整条命令行的路径字段留了 256 字节。
256 减 2 就是 254。
所以这个数字是「从命令行长度倒推出来的」,不是「地址本身的天性」。
理解这一点有个实际好处:
当你看到某个系统的邮箱字段限制成 100 字符时,那是「数据库设计者的选择」,不是「协议要求」。
很多「莫名其妙」的长度限制,都是这么来的。
顺带说一句「大小写」:域名部分大小写不敏感,本地部分理论上敏感。
所以 [email protected] 和 [email protected] 是同一个域里的两个不同信箱(理论上)。
但几乎所有服务商都按不敏感处理——因为「用户记不住自己注册时是大写还是小写」。
三、正则拦得住什么,拦不住什么
下面用的正则是 ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$,
这是「够用」的那一档,不是「正确」的那一档。
它拦得住:没有本地部分、没有域名点、两个 at、本地部分带空格。
它拦不住:真正的 RFC 允许 "a b"@c.com 这种带引号的写法,
也允许 IP 字面量 a@[192.0.2.1]。
这两种写法在现实里几乎没人用,但它们是合法的。
所以现实中的做法是「先做宽松的格式检查,再发一封验证邮件」——
因为格式合法不等于这个信箱存在。
这也是所有注册流程都要你「点邮件里的链接」的原因:
那是唯一能证明「这个地址真的能收信」的办法。
四、动手 Demo:邮件地址为什么不能随便写
先把四个长度上限摆出来,再用正则跑一批地址。 重点是看「哪些该拦的拦住了、哪些不该拦的也拦住了」。
import re
pat = re.compile(r'^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$')
cases = [
('[email protected]', '最短的合法地址'),
('[email protected]', '带加号和二级域'),
('a@b', '域名没有点'),
('a [email protected]', '本地部分有空格'),
('@c.com', '没有本地部分'),
('[email protected]', '域名以点开头'),
('a@@c.com', '两个 at'),
]
for addr, why in cases:
print('%-38s %-6s %s' % (addr, '通过' if pat.match(addr) else '拒绝', why))
实跑输出:
--------------------------------------------------------------------
邮件地址为什么不能随便写
--------------------------------------------------------------------
**邮件地址的形状是 `本地部分@域名`**,**看着简单,但每一段都有长度上限,而且上限不是随便定的。**
位置 上限 为什么是这个数
---------------------------------------------------------------
本地部分 64 字节 **RFC 5321 的 quoted-string 上限**
域名部分 255 字节 **受 DNS 名字总长限制**
整个地址 254 字节 **路径尖括号内的上限 256 减 2**
每一级标签 63 字节 **DNS 单个标签的硬限制**
**254 这个数值得推一遍**:**SMTP 命令里地址要写成 `<a@b>` 的形式,两侧尖括号占 2 字节。**
**而整条命令行的路径字段上限是 256 字节。**
**256 减 2 就是 254。**
**所以这个数字是「从命令行长度倒推出来的」,不是「地址本身的天性」。**
地址 正则判定 说明
--------------------------------------------------------------------
`[email protected]` **通过** 最短的合法地址
`[email protected]` **通过** 带加号和二级域
`a@b` **拒绝** 域名没有点
`a [email protected]` **拒绝** 本地部分有空格
`@c.com` **拒绝** 没有本地部分
`[email protected]` **拒绝** 域名以点开头
`a@@c.com` **拒绝** 两个 at
**这个正则只是「够用」,不是「正确」**:**真正的 RFC 允许 `"a b"@c.com` 这种带引号的写法**,
**也允许 IP 字面量 `a@[192.0.2.1]`。**
**现实中的做法是「先做宽松的格式检查,再发一封验证邮件」**——**因为格式合法不等于这个信箱存在。**
**还有一条容易忽略**:**域名部分大小写不敏感,本地部分理论上敏感。**
**所以 `[email protected]` 和 `[email protected]` 是同一个域里的两个不同信箱**(理论上)。
**但几乎所有邮件服务商都按不敏感处理**——**因为「用户记不住自己注册时是大写还是小写」。**
两条通过、五条拒绝。
被拒的五条里,有四条是「明显不对」,一条是「域名没有点」。
a@b 这一条值得单独说:在 DNS 里 b 是一个合法的顶级域吗?严格说不是(需要有那个 TLD),
但正则拦它是因为「没有点」,不是因为「TLD 不存在」。
这意味着「本地内部域名」的邮箱地址会被这个正则误拦——
比如公司内网用 zhangsan@intranet 这种写法。
所以「正则越严格越好」是个错觉:太严格会拦掉合法地址,太宽松会放进垃圾。
真正的验证只有一条:发一封信过去,看它能不能收到。
30 秒实验
- 算一下:
<和>各占一字节,256 减 2 是多少? - 想一下:为什么 DNS 单个标签最多 63 字节?
- 想一下:邮箱大小写不敏感,是协议要求还是实现选择?
常见误区
- 以为「254 是地址本身的天性」。 它是从命令行长度倒推的,换个提交协议这个数就变了。
- 以为「正则写得越严越安全」。 太严会拦掉合法地址,验证只能靠发信。
- 以为「邮件地址大小写敏感」。 域名部分不敏感,本地部分理论上敏感但实际都按不敏感处理。
自测三问(点开对答案)
1. 邮件地址的四段长度上限是多少? —— **本地部分 64、域名 255、整体 254、每级标签 63。** 2. 254 是怎么来的? —— **路径字段上限 256,减去两侧尖括号 2 字节。** 3. 怎么真正验证一个邮箱存在? —— **发一封验证邮件**,看对方能不能点开链接。下一节看 MIME 怎么让邮件装下中文和附件。
- ☐ (本节完,下一节 8.4.3 MIME)