Quoted-Printable 编解码

在可读文本和 =XX 形式之间互转,处理邮件正文的软换行、行尾空格和多字节字符。

编码规则

可打印的 ASCII(除等号)原样保留;等号写成 =3D,其余字节写成 = 加两位大写十六进制。中文按 UTF-8 变成三个字节,所以「中」会变成 =E4=B8=AD。

编码时按 76 列折行,行尾用 = 作为软换行标记,硬换行保留为 CRLF。行尾的空格和制表符必须编码,否则中间的邮件网关可能把它裁掉,解出来就少了一个空格。

解码时先把软换行去掉,再逐字节还原,最后按 UTF-8 解释。原文不是 UTF-8 时,Quoted-Printable 本身没有字符集信息,只能靠邮件头里的 charset 或人工判断。

处理 Base64 编码的邮件正文用Base64 编解码;要看整个邮件文件的结构,可以先用文本文件编辑器打开,再分段处理。

常见问题

邮件里为什么要用这种编码?

早期的邮件协议只保证 7 位 ASCII 安全传输,中文、表情和二进制都会被中间服务器改坏。Quoted-Printable 把非 ASCII 字节写成 =XX,其余保持原样,既兼容旧网关又保持一定的可读性。

行尾的等号是什么意思?

软换行。编码时一行最多 76 个字符,超长就在行尾放一个 = 再换行,解码时把 = 和换行一起丢掉,拼回原来的一行。它不是内容的一部分,看到行尾孤立的 = 不要当成字符。

为什么解码后中文是乱码?

Quoted-Printable 只负责把字节转成可打印形式,不记录字符集。原文如果是 GBK,按 UTF-8 解就会出替换字符。页面会同时给出 Windows-1252 的解释,但 GBK 需要用对应的解码器再转一次。

邮件头也能这样解吗?

邮件头用的是 RFC 2047 的编码字,形如 =?UTF-8?Q?...?=,里面的下划线代表空格。解码时勾上「下划线视为空格」,再去掉两端的 =?…?Q? 包装即可;正文不应该把下划线当空格。

空格和制表符为什么有时也被编码?

行尾的空格在传输中容易被裁掉,所以编码器会把出现在行尾的空格写成 =20、制表符写成 =09。页面按这个规则处理,行中的空格保持原样。

编码结果是等宽的吗?

不是。ASCII 字符占 1 个字符,中文按 UTF-8 是 3 个字节,编成 9 个字符。这也是为什么同样长度的中文和英文,编码后的体积差很多。