一、简介
核心原理是:编码时,一个字节被编码为如=[0-9A-F][0-9A-F]
形式,每个字节有两个“4位比特”,每一个“4位比特”对应一个[0-9A-F]
字符(即对应的十六进制字符);解码时,逆向化过程。
二、原理
2.1、编码
当“Media-Type”类型为“text”时,编码过程面对的是字符流,首先需根据某种编码方案获取字节流,编码方案比如有ASCII,GBK,UTF-8等;当“Media-Type”类型为“非text”时,编码过程面对的是字节流,无需额外操作。
接下来的描述中,以十六进制值形式代表字节。
2.1.1、编码规则
2.1.1.1、规则1
编码时,一个字节被编码为如=[0-9A-F][0-9A-F]
形式,每个字节有两个“4位比特”,每一个“4位比特”对应一个[0-9A-F]
字符(即对应的十六进制字符)。
实验:
字节流 | 编码后结果字符串 |
---|---|
0x01,0x09,0x0D,0x0A,0x1F,0x20,0x21,0x22,0x3D,0x40,0x41,0x7F | =01=09=0D=0A=1F=20=21=22=3D=40=41=7F |
备注:
“规则1”是默认编码规则,未在“规则2-6”中指明的情形都要按照“规则1”进行编码。
2.1.1.2、规则2
位于“0x21-0x3C”和“0x3E-0x7E”范围之内的字节不按照“规则1”进行编码,而以对应的ASCII字符(此时的ASCII字符都属于“可打印字符”)作为编码后结果字符。
实验:
字节流 | 编码后结果字符串 |
---|---|
0x01,0x09,0x0D,0x0A,0x1F,0x20,0x21,0x22,0x3D,0x40,0x41,0x7F | =01=09=0D=0A=1F=20!"=3D@A=7F |
2.1.1.3、规则3
“0x09”和“0x20”这两个字节一般以对应的ASCII字符(“0x09”对应“TAB”字符,“0x20”对应“空格”字符)作为编码后结果字符,但当上述的编码后结果字符最终会位于编码后结果字符串的末尾时,这两个字节必须按照“规则1”进行编码。
实验:
参见“规则5”下实验。
2.1.1.4、规则4
编码后结果字符串需经过一系列中间处理设备,为了更好的健壮性,推荐对于如下字节集合中的字节按照“规则1”进行编码,而不是遵循“规则2”:
字节集合:0x21,0x22,0x23,0x24,0x40,0x5B,0x5C,0x5D,0x5E,0x60,0x7B,0x7C,0x7D,0x7E
与上面字节集合一一对应的ASCII字符集合:!,",#,$,@,[,,],^,`,{,|,},~
实验:
字节流 | 编码后结果字符串 |
---|---|
0x01,0x09,0x0D,0x0A,0x1F,0x20,0x21,0x22,0x3D,0x40,0x41,0x7F | =01=09=0D=0A=1F=20=21=22=3D=40A=7F |
2.1.1.5、规则5
编码后结果字符串的长度需要满足“小于等于76”,可使用单独的“=”字符进行分割,此时的“=”字符被称为“软换行符”。
实验:
字节流 | 编码后结果字符串 |
---|---|
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39, 0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A, 0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74, 0x75,0x76,0x77,0x78,0x79,0x7A,0x20,0x41,0x42,0x43, 0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D, 0x4E,0x4F,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, 0x58,0x59,0x5A,0x30,0x31,0x32,0x33,0x34,0x35,0x36, 0x37,0x38,0x39,0x61,0x62,0x20 |
0123456789abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789= |
备注:
上述实验中的字节流在实际实验中并没有换行,这里只是为了展示方便而进行了换行。
2.1.1.6、规则6
有这样的一个基本背景:RFC2045规范针对“邮件”应用场景,一封“邮件”需要经过一系列中间处理设备,不同设备对“换行符”的定义不尽相同,因此,原始的“CRLF”字符对最后可能被转换成“CR”或者“LF”单个字符。
有如下具体规则:
- 当邮件的“Media-Type”类型为“text”时,编码过程面对的是“字符流”,以字符流中的“CRLF”字符对为分界符进行分段,对每段字符流先获取字节流,再按照“规则1-5”进行编码
- 当邮件的“Media-Type”类型为“非text”时,编码过程面对的是“字节流”,无需特殊处理“0x0D,0x0A”字节对(与之相应的ASCII字符对即为“CRLF”),直接按照“规则1-5”进行编码
具体原因如下:
- 当邮件的“Media-Type”类型为“text”时,保留“CRLF”字符对可以减小编码后结果字符串的长度,减少传输内容,而且即便“CRLF”字符对最后被转换成“CR”或者“LF”单个字符也没有丢失有意义的数据
- 当邮件的“Media-Type”类型为“非text”时,如果特殊处理“0x0D,0x0A”字节对,将其编码成“CRLF”字符对,那么当“CRLF”字符对最后被转换成“CR”或者“LF”单个字符时,就会丢失有意义的数据
2.1.2、合法编码后结果字符串描述
quoted-printable=qp-line*((CRLF)(qp-line))
qp-line=*((qp-segment)(transport-padding)(CRLF))(qp-part)(transport-padding)
qp-segment=(qp-section)*(SPACE/TAB)”=” ;小于等于76个字符
qp-section=[*(ptext/SPACE/TAB)ptext]
ptext=hex-octet/safe-char
hex-octet=”=”2(DIGIT/“A”/“B”/“C”/“D”/“E”/“F”)
safe-char=< ASCII表中字符,对应“规则2”,十六进制值范围为“0x21-0x3C”和“0x3E-0x7E”,对应“规则4”,再额外排除!"#$@[\]^`{|}~
字符集合 >
transport-padding=*LWSP-char
qp-part=qp-section ;小于等于76个字符
2.2、解码
编码过程的逆向化过程。
参考文献: [1]http://www.rfc-editor.org/rfc/rfc2045.txt