前面的话
Python内置的base64模块,在这里http://docs.python.org/library/base64.html?highlight=base64#base64,包括b64encode,b64decode,urlsafe_b64decode等,可以满足包括URL在内的文本编码需要。但是在用base64.encode编码二进制文件的时候,发现编码不完整,只有部分文件被编码了,base64.decode解码出来文件错误。可能是base64模块用来出来文本的?仔细分析发现,是忘记用二进制模式打开文件了。但是,自己实现base64模块基本功能也不是什么难事,不是要重复发明*,仅作为学习python和base64的练习。
用内置 base64模块转换二进制文件与base64编码文本文件方法如下:
import base64 fin = open(r"D:\2.zip", "rb") fout = open(r"D:\2.x.txt", "w") base64.encode(fin, fout) fin.close() fout.close() fin = open(r"D:\2.x.txt", "r") fout = open(r"D:\2.x.zip", "wb") base64.decode(fin, fout) fin.close() fout.close()
Base64介绍
Base64是一种基于64个可打印字符来表示二进制数据的表示方法。Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据。包括MIME的email,email via MIME, 在XML中存储复杂数据。
转换的时候,将三个byte的数据,先后放入一个24bit的缓冲区中,先来的byte占高位。数据不足3byte的话,于缓冲区中剩下的bit用0补足。然后,每次取出6(因为)个bit,按照其值选择ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
中的字符作为编码后的输出。不断进行,直到全部输入数据转换完成。
如果最后剩下两个输入数据,在编码结果后加1个“=”;如果最后剩下一个输入数据,编码结果后加2个“=”;如果没有剩下任何数据,就什么都不要加,这样才可以保证资料还原的正确性。
Base64索引表:
Value |
Char |
|
Value |
Char |
|
Value |
Char |
|
Value |
Char |
0 |
A |
16 |
Q |
32 |
g |
48 |
w |
|||
1 |
B |
17 |
R |
33 |
h |
49 |
x |
|||
2 |
C |
18 |
S |
34 |
i |
50 |
y |
|||
3 |
D |
19 |
T |
35 |
j |
51 |
z |
|||
4 |
E |
20 |
U |
36 |
k |
52 |
0 |
|||
5 |
F |
21 |
V |
37 |
l |
53 |
1 |
|||
6 |
G |
22 |
W |
38 |
m |
54 |
2 |
|||
7 |
H |
23 |
X |
39 |
n |
55 |
3 |
|||
8 |
I |
24 |
Y |
40 |
o |
56 |
4 |
|||
9 |
J |
25 |
Z |
41 |
p |
57 |
5 |
|||
10 |
K |
26 |
a |
42 |
q |
58 |
6 |
|||
11 |
L |
27 |
b |
43 |
r |
59 |
7 |
|||
12 |
M |
28 |
c |
44 |
s |
60 |
8 |
|||
13 |
N |
29 |
d |
45 |
t |
61 |
9 |
|||
14 |
O |
30 |
e |
46 |
u |
62 |
+ |
|||
15 |
P |
31 |
f |
47 |
v |
63 |
/ |
二进制转成Base64编码
按照算法描述:
1、讲输入数据按3个字节组成一组3*8=24位的整数;
2、然后将其按6位一组分为24/6=4组;
3、每组6位算出取值,在base64索引表中查看对应的字符,即可;
4、如果还有模3剩余的1个字节数据,则补2个字节的0,将转换成的4字符的最后2个替换成”==”;
5、如果还有模3剩余的2个字节数据,则补1个字节的0,将转换成的4字符的最后1个替换成”=”。
其中1-3步容易理解和编写,第4、5步的实现方法可能有很多种,但是考虑到可读性,而且每次转换之多执行其中一步,可以使用如下中的硬编码:
_CODE_CHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" def binbase64(data): """ Convert binary data to Base64 format string. """ base64str = "" for i in range(len(data)/3): datavalue = ((data[3*i] << 16) | (data[3*i+1] << 8) | data[3*i+2]) for j in range(4): base64str += _CODE_CHAR[(datavalue >> 6*(3-j)) & 0x3F] dataremain = len(data) % 3 if dataremain == 1: datavalue = data[-1] << 16; base64str += _CODE_CHAR[(datavalue >> 18) & 0x3F] base64str += _CODE_CHAR[(datavalue >> 12) & 0x3F] base64str += "==" elif dataremain == 2: datavalue = (data[-2] << 16) | (data[-1] << 8); base64str += _CODE_CHAR[(datavalue >> 18) & 0x3F] base64str += _CODE_CHAR[(datavalue >> 12) & 0x3F] base64str += _CODE_CHAR[(datavalue >> 6) & 0x3F] base64str += "=" return base64str
Base64解码转成二进制
解码之前要对输入数据有效性进行必要的判断,如长度是否为4的整数倍,有没有非法字符等。解码步骤为:
1、取4个字符组成一组,判断每个字符在Base64索引表中的索引值;
2、将索引值转成6位二进制,并组4*6=24位的整数;
3、再将这个24位的整数分成24/8=3个字节;
4、如果末尾有2个"==",则只有1个有效字节
5、如果末尾有1个"=",在有2个有效字节
def base64bin(encodedstr): """ Convert Base64 format string to binary data. """ if len(encodedstr) % 4: raise Base64Error("The length of input 'base64str' MUST be multiple of 4.") rawbase64str = encodedstr.rstrip("=") if (len(rawbase64str) % 4) == 1: raise Base64Error("Too many '=' characters, MUST NOT be more than 2.") for x in rawbase64str: if x not in _CODE_CHAR: raise Base64Error("Unexpected character %s.", x) data=[] for i in range(len(rawbase64str)/4): datavalue = (_CODE_CHAR.find(rawbase64str[4*i]) << 18) \ | (_CODE_CHAR.find(rawbase64str[4*i+1]) << 12) \ | (_CODE_CHAR.find(rawbase64str[4*i+2]) << 6) \ | (_CODE_CHAR.find(rawbase64str[4*i+3])) data.append((datavalue >> 16) & 0xFF) data.append((datavalue >> 8) & 0xFF) data.append((datavalue) & 0xFF) strremain = len(rawbase64str) % 4 if strremain == 2: datavalue = (_CODE_CHAR.find(rawbase64str[-2]) << 18) \ | (_CODE_CHAR.find(rawbase64str[-1]) << 12) data.append((datavalue >> 16) & 0xFF) elif strremain == 3: datavalue = (_CODE_CHAR.find(rawbase64str[-3]) << 18) \ | (_CODE_CHAR.find(rawbase64str[-2]) << 12) \ | (_CODE_CHAR.find(rawbase64str[-1]) << 6) data.append((datavalue >> 16) & 0xFF) data.append((datavalue >> 8) & 0xFF) return data
class Base64Error(Exception): """ Exception for Base64 error. """ pass
字符串转换
def strbase64(astr): """ Convert a string to Base64 format string. """ return binbase64(map(ord, astr))
def base64str(encodedstr): """ Convert Base64 format string to a string. """ return "".join(map(chr,base64bin(encodedstr)))
文件转换
文件转换要用到这里介绍的filehleper。另外,RFC 822规定,Base64文本没行76个字符,可存76/4*3=57个字节。所以我们每次读入57个字节处理。
def binfiletobase64(inp, out): """ Convert binary file to Base64 format text file. """ blocksize = 76 / 4 * 3 def _binfiletobase64(fin, fout): while True: chunk = fin.read(blocksize) if chunk: fout.write(strbase64(chunk)) fout.write("\n") else: break fileinoutpattern(inp, out, _binfiletobase64, inmode="rb", outmode="w") def base64filetobin(inp, out): """ Convert Base64 format text file to binary file. """ def _base64filetobin(fin, fout): for line in fin: fout.write(base64str(line.rstrip())) fileinoutpattern(inp, out, _base64filetobin, inmode="r", outmode="wb")
测试代码
def test(): """ Self testing. """ rawstr = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure." encodedstr = binbase64(map(ord, rawstr)) for x in range(0, len(encodedstr), 76): print encodedstr[x:x+76] encodedstr = strbase64(rawstr) for x in range(0, len(encodedstr), 76): print encodedstr[x:x+76] data = base64bin(encodedstr) decodedstr = "".join(map(chr,data)) print decodedstr assert decodedstr == rawstr decodedstr = base64str(encodedstr) print decodedstr assert decodedstr == rawstr #base64str(encodedstr[:-1]);#Not multiple of 4 #base64str(encodedstr[:-3]+"==");#Too many '=' #base64str(encodedstr[:-2]+"()");#Invaild characters '(' and ')' binfiletobase64(r"D:\2.zip", r"D:\2.txt") base64filetobin(r"D:\2.txt", r"D:\2.1.zip") print "OK" if __name__ == "__main__": test()
下载地址1:源代码下载binbase64.zip
下载地址2:源代码下载binbase64.zip