Python使用base64模块进行二进制数据编码详解
前言
昨天团队的学妹来问关于POP3协议的问题,所以今天稍稍研究了下POP3协议的格式和Python里面的poplib。而POP服务器往回传的数据里有一部分需要用到Base64进行解码,所以就顺便看了下Python里面的base64模块。
本篇先讲一下base64模块,该模块提供了关于Base16,Base32,Base64,Base85和Ascii85的编码和解码相关的函数。有关poplib模块的内容,会在后面发上来。嗯,又挖了一个坑,这辈子挖的坑填不完了...
以下内容摘自http://bbs.chinaunix.net/thread-1150250-1-1.html,详细说明了为什么回传的数据会先经过Base64编码:
由於歷史原因,Internet上有些郵件系統只支援7Bit的字元傳輸,而漢字的內碼是8Bit的,當在電子郵件中發送中文時,如果經過這些只支援7Bit字元的郵件系統,便會將漢字內碼的第八位元的1全部變成0。
以”中文”兩字為例,HEX為A4A4A4E5,當最高位元被清掉時就會變成24242465,也就是”$$$e”。telnet也存在這樣子的問題。
除了中文郵件外,使用電子郵件傳送圖片、程式、壓縮文件等也會發生這個問題。所以在電子郵件中一般採用各種郵件編碼方式來解決這個問題,將8Bit按照一定的規則進行編碼,便可以完好地通過只支持7Bit字元的郵件系統。
常見的郵件編碼有UU與MIME,而MIME(Multipurpose Internet Mail Extentions)一般翻譯成「多媒體傳送模式」,顧名思義,它標榜的就是可以傳送多媒體型式的檔案,可以在一封mail中附加各種型式檔案一起送出。
MIME定義兩種編碼方法:Base64與QP(Quote-Printable),兩者使用時機不同,QP的規則是對於資料中的7bits無須重複encode,僅8bits資料轉成7bits。QP編碼適用於非US-ASCII的文字內容,例如我們的中文檔案,而Base64的編碼規則,是將整個檔案重新編碼,編成7bits,它是用於傳送binary檔案時使用。由於編碼的方式不同,會影響編碼之後的檔案大小。有些較懶惰的軟體便都一律採用Base64編碼了。
Base64
base64模块提供了6个函数用于Base64的编码和解码,可以将他们分为三组。
base64.b64encode(s, altchars=None)
base64.b64decode(s, altchars=None, validate=False)
参数s代表需要编码/解码的数据。其中b64encode的参数s的类型必须是字节包(bytes)。b64decode的参数s可以是字节包(bytes),也可以是字符串(str)。
由于Base64编码后的数据中可能会含有'+'或者'/'两个符号,如果编码后的数据用于url或者文件系统的路径中,就可能会导致Bug。所以base64模块提供了将编码后的数据中'+'和'/'进行替换的方法。
参数altchars必须是长度为2的字节包,这两个符号会用于替换编码后数据中的'+'和'/'。这个参数默认是None。
参数validate默认为False。如果它为True时,base64模块在进行解码前会先检查s中是否有非base64字母表中的字符,如果有的话则抛出错误binascii.Error: Non-base64 digit found。
如果数据的长度不正确则会抛出错误binascii.Error: Incorrect padding。
>>> import base64
>>> x = base64.b64encode(b'test')
>>> x
b'dGVzdA=='
>>> base64.b64decode(x)
b'test'
base64.standard_b64encode(s)
base64.standard_b64decode(s)
这组函数会直接将参数s传到上一组函数中。
base64.urlsafe_b64encode(s)
base64.urlsafe_b64decode(s)
这组函数同样基于第一组函数,但进行编码后会将输出数据中的'+'和'/'替换为'-‘和'_'。解码前则将数据中的'-‘和'_'替换为'+'和'/'。
另,Base64编码还会产生一个符号'=',这个符号用于将数据长度填充到4的倍数。
Base32
base64.b32encode(s)
base64.b32decode(s, casefold=False, map01=None)
参数s与Base64一致。
Base32编码后的字符范围为[2-7A-Z],是不支持小写字母的。不过当参数casefold为True时,Base32解码时可以接受小写字母的输入。但是为了安全考虑,这个参数默认为False。
Base32的解码同时还允许将数字0替换为大写字母O,把数字1替换为大写字母I或者L。参数map01可以指定将数字1替换为哪个字符(源码中并没有限定必须是字母I或者L其中之一),当这个参数非None时,数字0总是会被替换为字母O。同样为了安全考虑,这个参数默认为None。
Base16
base64.b16encode(s)
base64.b16decode(s, casefold=False)
Base16编码后的字符范围为[0-9A-F]。
参数s和casefold的作用与Base32一致。
Base85
base64.b85encode(b, pad=False)
base64.b85decode(b)
参数b为用于编码/解码的数据,类型要求跟Base64的参数s一致。
参数pad为True时,在编码前会用b'\0'将数据填充到长度为4的倍数。不过在解码的时候不会移除这些填充数据。
这组函数是在Python3.4之后新增的。
Ascii85
base64.a85encode(b, *, foldspaces=False, wrapcol=0, pad=False, adobe=False)
参数b为用于编码的数据,类型必须为bytes。
参数foldspaces为True时会用b'y'来表示4个连续的空格。
参数wrapcol为一个整数,当wrapcol非0时,这个整数控制编码后的输出每多少个字符添加一个换行符b'\n'。
参数pad为True时,数据在编码前会用b'\0'填充到长度为4的倍数。解码的时候不会移除这些填充数据。
参数adobe指定了数据是否采用Adobe的格式。Adobe Ascii85的编码数据由<\~和\~>包围起来,如果这个参数为True,返回的数据会加上这对符号。
base64.a85decode(b, *, foldspaces=False, adobe=False, ignorechars=b' \t\n\r\v')
参数b为用于编码的数据,类型可以为bytes或者str。
参数foldspaces为True时会用b'y'来表示4个连续的空格。
参数adobe指定了数据是否采用Adobe的格式。Adobe Ascii85的编码数据由<\~和\~>包围起来,如果这个参数为True,解码前base64会先去掉这对符号。
参数ignorechars指定了解码时需要忽略掉的字符。默认包含了ASCII中所有的空白符。
这组函数是在Python3.4之后新增的。
base64模块的官方文档中提到:Base85和Ascii85使用5个字符编码4个字节,而Base64使用6个字符编码4个字节(实际上是4个字符编码3个字节),当空间不充裕时前两者会比Base64更高效。
旧API
base64仍然保留了一部分旧的API,用于一些特殊用途。
base64.encode(input, output)
base64.decode(input, output)
这组函数使用二进制文件作为数据源,并将编码/解码后的数据写入二进制文件。
base64.encodebytes(s)
base64.decodebytes(s)
encodebytes和b64encode在内部都是调用的binascii模块的b2a_base64,只不过encodebytes调用b2a_base64时newline参数使用默认值True。也就是说,encodebytes在输出数据的时候,每76个字节会添加一个换行符b'\n'。
decodebytes和默认参数下的b64decode基本一致。只有参数类型的检查不一样,decodebytes只支持bytes类型的数据。
base64.encodestring(s)
base64.decodestring(s)
这组函数在Python3.1之后就废弃了,目前会直接调用上一组函数。
总结
base64模块提供了对二进制数据进行编码的接口,其中包括了标准的Base64,Base32,Base16和事实标准Ascii85和Base85。通过学习这个模块,顺便学习了一下二进制数据编码的各种细节,感受颇深。有时候我们自以为了解计算机,了解互联网,其实每个人看到的都只是沧海一粟,不值一提。这个领域对于我来说还有很多未知,是等待探索的,而我也不会停止探索的脚步。
以上是 Python使用base64模块进行二进制数据编码详解 的全部内容, 来源链接: utcz.com/z/336821.html