【Python】二进制文件与Base64编码文本文件转换

前面的话

Python内置的base64模块,在这里http://docs.python.org/library/base64.html?highlight=base64#base64包括b64encodeb64decodeurlsafe_b64decode等,可以满足包括URL在内的文本编码需要。但是在用base64.encode编码二进制文件的时候,发现编码不完整,只有部分文件被编码了,base64.decode解码出来文件错误。可能是base64模块用来出来文本的?仔细分析发现,是忘记用二进制模式打开文件了。但是,自己实现base64模块基本功能也不是什么难事,不是要重复发明轮子,仅作为学习pythonbase64的练习。

用内置 base64模块转换二进制文件与base64编码文本文件方法如下:

[python]  view plain  copy
 print ?
  1. import base64  
  2. fin = open(r"D:\2.zip""rb")  
  3. fout = open(r"D:\2.x.txt""w")  
  4. base64.encode(fin, fout)  
  5. fin.close()  
  6. fout.close()  
  7.   
  8. fin = open(r"D:\2.x.txt""r")  
  9. fout = open(r"D:\2.x.zip""wb")  
  10. base64.decode(fin, fout)  
  11. fin.close()  
  12. fout.close()  

Base64介绍

Base64是一种基于64个可打印字符来表示二进制数据的表示方法。Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据。包括MIMEemailemail via MIME, XML中存储复杂数据。

转换的时候,将三个byte的数据,先后放入一个24bit的缓冲区中,先来的byte占高位。数据不足3byte的话,于缓冲区中剩下的bit0补足。然后,每次取出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步容易理解和编写,第45步的实现方法可能有很多种,但是考虑到可读性,而且每次转换之多执行其中一步,可以使用如下中的硬编码:

[python]  view plain  copy
 print ?
  1. _CODE_CHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"  
  2.   
  3. def binbase64(data):  
  4.     """ 
  5.     Convert binary data to Base64 format string. 
  6.     """  
  7.     base64str = ""  
  8.   
  9.     for i in range(len(data)/3):  
  10.         datavalue = ((data[3*i] << 16) | (data[3*i+1] << 8) | data[3*i+2])  
  11.         for j in range(4):  
  12.             base64str += _CODE_CHAR[(datavalue >> 6*(3-j)) & 0x3F]  
  13.       
  14.     dataremain = len(data) % 3  
  15.     if dataremain == 1:  
  16.         datavalue = data[-1] << 16;  
  17.         base64str += _CODE_CHAR[(datavalue >> 18) & 0x3F]  
  18.         base64str += _CODE_CHAR[(datavalue >> 12) & 0x3F]  
  19.         base64str += "=="  
  20.     elif dataremain == 2:  
  21.         datavalue = (data[-2] << 16) | (data[-1] << 8);  
  22.         base64str += _CODE_CHAR[(datavalue >> 18) & 0x3F]  
  23.         base64str += _CODE_CHAR[(datavalue >> 12) & 0x3F]  
  24.         base64str += _CODE_CHAR[(datavalue >> 6) & 0x3F]  
  25.         base64str += "="  
  26.     return base64str  

Base64解码转成二进制

解码之前要对输入数据有效性进行必要的判断,如长度是否为4的整数倍,有没有非法字符等。解码步骤为:

1、取4个字符组成一组,判断每个字符在Base64索引表中的索引值;

2、将索引值转成6位二进制,并组4*6=24位的整数;

3、再将这个24位的整数分成24/8=3个字节;

4、如果末尾有2个"==",则只有1个有效字节

5、如果末尾有1个"=",在有2个有效字节

[python]  view plain  copy
 print ?
  1. def base64bin(encodedstr):  
  2.     """ 
  3.     Convert Base64 format string to binary data. 
  4.     """  
  5.     if len(encodedstr) % 4:  
  6.         raise Base64Error("The length of input 'base64str' MUST be multiple of 4.")  
  7.       
  8.     rawbase64str = encodedstr.rstrip("=")  
  9.   
  10.     if (len(rawbase64str) % 4) == 1:  
  11.         raise Base64Error("Too many '=' characters, MUST NOT be more than 2.")  
  12.   
  13.     for x in rawbase64str:  
  14.         if x not in _CODE_CHAR:  
  15.             raise Base64Error("Unexpected character %s.", x)  
  16.       
  17.     data=[]  
  18.     for i in range(len(rawbase64str)/4):  
  19.         datavalue = (_CODE_CHAR.find(rawbase64str[4*i]) << 18) \  
  20.                     | (_CODE_CHAR.find(rawbase64str[4*i+1]) << 12) \  
  21.                     | (_CODE_CHAR.find(rawbase64str[4*i+2]) << 6) \  
  22.                     | (_CODE_CHAR.find(rawbase64str[4*i+3]))  
  23.         data.append((datavalue >> 16) & 0xFF)  
  24.         data.append((datavalue >> 8) & 0xFF)  
  25.         data.append((datavalue) & 0xFF)  
  26.       
  27.     strremain = len(rawbase64str) % 4  
  28.     if strremain == 2:  
  29.         datavalue = (_CODE_CHAR.find(rawbase64str[-2]) << 18) \  
  30.             | (_CODE_CHAR.find(rawbase64str[-1]) << 12)  
  31.         data.append((datavalue >> 16) & 0xFF)  
  32.     elif strremain == 3:  
  33.         datavalue = (_CODE_CHAR.find(rawbase64str[-3]) << 18) \  
  34.             | (_CODE_CHAR.find(rawbase64str[-2]) << 12) \  
  35.             | (_CODE_CHAR.find(rawbase64str[-1]) << 6)  
  36.         data.append((datavalue >> 16) & 0xFF)  
  37.         data.append((datavalue >> 8) & 0xFF)  
  38.   
  39.     return data  
[python]  view plain  copy
 print ?
  1. class Base64Error(Exception):  
  2.     """ 
  3.     Exception for Base64 error. 
  4.     """  
  5.     pass  

字符串转换

[python]  view plain  copy
 print ?
  1. def strbase64(astr):  
  2.     """ 
  3.     Convert a string to Base64 format string. 
  4.     """  
  5.     return binbase64(map(ord, astr))  
[python]  view plain  copy
 print ?
  1. def base64str(encodedstr):  
  2.     """ 
  3.     Convert Base64 format string to a string. 
  4.     """  
  5.     return "".join(map(chr,base64bin(encodedstr)))  

文件转换

文件转换要用到这里介绍的filehleper。另外,RFC 822规定,Base64文本没行76个字符,可存76/4*3=57个字节。所以我们每次读入57个字节处理。

[python]  view plain  copy
 print ?
  1. def binfiletobase64(inp, out):  
  2.     """ 
  3.     Convert binary file to Base64 format text file. 
  4.     """  
  5.     blocksize = 76 / 4 * 3  
  6.     def _binfiletobase64(fin, fout):  
  7.         while True:  
  8.             chunk = fin.read(blocksize)  
  9.             if chunk:  
  10.                 fout.write(strbase64(chunk))  
  11.                 fout.write("\n")  
  12.             else:  
  13.                 break  
  14.       
  15.     fileinoutpattern(inp, out, _binfiletobase64, inmode="rb", outmode="w")  
  16.   
  17. def base64filetobin(inp, out):  
  18.     """ 
  19.     Convert Base64 format text file to binary file. 
  20.     """  
  21.     def _base64filetobin(fin, fout):  
  22.         for line in fin:  
  23.             fout.write(base64str(line.rstrip()))  
  24.       
  25.     fileinoutpattern(inp, out, _base64filetobin, inmode="r", outmode="wb")  

测试代码

[python]  view plain  copy
 print ?
  1. def test():  
  2.     """ 
  3.     Self testing. 
  4.     """  
  5.     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."  
  6.     encodedstr = binbase64(map(ord, rawstr))  
  7.     for x in range(0, len(encodedstr), 76):  
  8.         print encodedstr[x:x+76]  
  9.       
  10.     encodedstr = strbase64(rawstr)  
  11.     for x in range(0, len(encodedstr), 76):  
  12.         print encodedstr[x:x+76]  
  13.       
  14.     data = base64bin(encodedstr)  
  15.     decodedstr =  "".join(map(chr,data))  
  16.     print decodedstr  
  17.     assert decodedstr == rawstr  
  18.       
  19.     decodedstr = base64str(encodedstr)  
  20.     print decodedstr  
  21.     assert decodedstr == rawstr  
  22.   
  23.     #base64str(encodedstr[:-1]);#Not multiple of 4  
  24.     #base64str(encodedstr[:-3]+"==");#Too many '='  
  25.     #base64str(encodedstr[:-2]+"()");#Invaild characters '(' and ')'  
  26.   
  27.     binfiletobase64(r"D:\2.zip", r"D:\2.txt")  
  28.     base64filetobin(r"D:\2.txt", r"D:\2.1.zip")  
  29.     print "OK"  
  30.   
  31. if __name__ == "__main__":  
  32.     test()  



下载地址1:源代码下载binbase64.zip

下载地址2:源代码下载binbase64.zip

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值