自己用 python 实现 base64 编码

base64 编码原理

二进制文件中包含有很多无法显示和打印的字符,二进制的数据一般以 ASCII 码形式(8 bit,即一个字节)存储,8 bit 可以表示 128 个不同的编码,而 ASCII 码中有 33 个编码表示的不是显示或打印的字符:

图片来自维基百科

剩下的编码表示的是可以打印的字符:

图片来自维基百科

当处理二进制文件中的数据时,就需要将无法显示或打印的字符进行转换,Base64 编码的原理就是将这 128 个不同的编码(可以打印或不可打印的字符)映射到 64 个可以打印的字符集中。

准备字符数组/字符串

首先准备 64 个可以显示/打印的字符数组(字符串),可以用 chr 将十进制数据转换成相应的字符,然后构造成字符数组:

def constructTable():
array = []
for i in range( 65, 91 ):
array.append( chr( i ) )
for i in range( 97, 123 ):
array.append( chr( i ) )
for i in range( 0, 10 ):
array.append( str( i ) )
array.append( '+' )
array.append( '-' )
# print( array )
return array

也可以用 string 提供的常量构造出一个字符串:

def constructTable2():
str = string.ascii_uppercase + string.ascii_lowercase + string.digits
return str + '+' + '-'

两者取出相应位置的字符都可以用数组的形式,比如用 table 保存字符数组/字符串,table[2] 就是 C

处理数据

接下来对二进制数据进行处理,每 3 个字节一组进行处理即可:

图片来自廖雪峰的官方教程

只考虑数据字节数为 3 的情况,将其重新编码:

def _b64encode_str( s0, s1, s2 ):
"""
s0、s1、s2 依次为第一、二、三个字符
"""
d = s2 & 63
d = array[ d ]
c1 = ( s1 & 15 ) << 2
c2 = ( s2 & 192 ) >> 6
c = c1 + c2
c = array[ c ]
b1 = ( s0 & 3 ) << 4
b2 = ( s1 & 240 ) >> 4
b = b1 + b2
b = array[ b ]
a = ( s0 & 252 ) >> 2
a = array[ a ]
return ''.join( [ a, b, c, d ] )

这里的思路是从右往左,依次计算出 d、c、b、a,也就是对应着上图的 n4、n3、n2、n1。当要编码的数据不是 3 的倍数时,需要在数据末尾用 \x00 补足成 3 的倍数,最后根据补 \x00 的次数在编码后的字符串中添加相应个数的 =

# input is str
length = len( str )
remainder = length % 3 # fill with zero
if( remainder == 1 ):
str = str + b'\x00\x00' # add twice
length += 2
elif( remainder == 2 ):
str = str + b'\x00' # add once
length += 1

之后,再将原始数据进行编码,先考虑简单的 remainder == 0 的情况,每 3 个字符一组进行编码即可:

i = 0
buf = StringIO()
while i < length:
en = _b64encode_str( str[ i ], str[ i+1 ], str[ i+2 ] )
buf.write( en )
i += 3

如果 remainder != 0,那么最后的三个字符中有添加的 =,这三个字符需要特殊处理,前面的字符和上面的处理方式一样,在最后返回的时候调用字符串的 encode 方法将其转为二进制:

while i < length - 3:
en = _b64encode_str( str[ i ], str[ i+1 ], str[ i+2 ] )
buf.write( en )
i += 3
# print( remainder, i, buf.getvalue() )
en = _b64encode_str( str[ i ], str[ i+1 ], str[ i+2 ] )
buf.write( en[ 0 ] )
buf.write( en[ 1 ] ) if( remainder == 2 ):
buf.write( en[ 2 ] ) # add once
buf.write( '=' )
elif( remainder == 1 ):
buf.write( '==' ) # add twice

然后编写一个简单的测试文件,简单验证下自己编写的 b64encode 方法是否正确:

def randomString():
# print( chars )
size = random.randint( 70, 100 )
rstr = ''.join( random.SystemRandom().choices( _CHARS, k = size ) )
return rstr.encode() def compare():
rstr = randomString()
exp = base64.b64encode( rstr )
act = mybase64.b64encode( rstr )
if( exp != act ):
print( rstr )
print( exp )
print( act )
raise ValueError loops = 10000
print( 'encode comp: ', timeit.timeit( stmt = compare, number = loops ) )

按照标准的 Base64 编码编写的代码没有问题。

性能比较

最后将 Python 自带的 base64 编码和自己编写的编码函数进行比较:

def encode1():
rstr = randomString()
base64.b64encode( rstr ) def encode2():
rstr = randomString()
mybase64.b64encode( rstr ) loops = 10000
print( sys.version ) print( 'random: ', timeit.timeit( randomString, number = loops ) )
print( 'encode1: ', timeit.timeit( stmt = encode1, number = loops ) )
print( 'encode2: ', timeit.timeit( stmt = encode2, number = loops ) )

输出结果如下:

小结

可以看到,自己编写的编码方法用时大约 0.447 seconds, base64 库提供的方法的用时约为 0.030 seconds,性能差距约 15 倍。所以一般没有必要自己实现 base64 编码。

另外测试中相应的 decode 方法是没有实现的,实现起来也比较简单,按照编码的方式反过来做就好了。

代码地址:github

Notable

  1. python 中 str 对象执行 encode 方法后字符串将会以二进制形式保存
  2. chr( 1 ) 返回值是 '\x01',对应的是不可打印的字符,str( 1 ) 返回值是 '1',是可以打印的字符。

Reference

  1. convert-string-to-binary-in-python
  2. Python 语言中的按位运算
  3. 与 Java SrtingBuffer 等效的 python 对象
  4. 随机字符串
  5. 廖雪峰的 Python 教程

自己用 python 实现 base64 编码的更多相关文章

  1. Python 模拟 Base64编码

    Base64编码原理:https://blog.csdn.net/wo541075754/article/details/81734770 def Enbs64(s): # 编码后的结果 result ...

  2. Python 中 base64 编码与解码

    base64 是经常使用的一种加密方式,在 Python 中有专门的库支持. 本文主要介绍在 Python2 和 Python3 中的使用区别: 在 Python2 环境: Python 2.7.16 ...

  3. python中base64编码与解码

    在python3中用base64进行编码和解码的时候特别注意: 题目要求: 准备一张.jpg图片,比如:mm.jpg,读取图片数据并通过b85encode加密之后写入到新文件mm.txt文件中,然后读 ...

  4. Python使用base64编码的问题

    有的时候,在base64解码的时候,由于字节问题出现解码错误.解决的办法就是不足原base64子串的长度: def decode_base64(data): """ De ...

  5. python裁剪base64编码的图片

    简介 今天遇到需要裁剪base64字符串的PNG图片,并返回base64格式字符串的任务,捣鼓半天. 裁剪代码如下: def deal_inspect_img(base64_str): "& ...

  6. Python Base64 编码

    0x00 Base64简介 0x01 常用场景举例 0x02 编.解码流程 0x03 Python中Base64编码与解码 0x00 Base64简介 我们知道在计算机中任何数据都是按ascii码存储 ...

  7. Python base64编码,转图片

    我在做火车票抢票器的时候遇到一个问题,就是验证码提取的:一般验证码都是一些http请求的url,但是火车票网站遇到了我没有见过的以data:image/jpg;base64开头的字符串.现在我们就用P ...

  8. Python中进行Base64编码和解码

    Base64编码 广泛应用于MIME协议,作为电子邮件的传输编码,生成的编码可逆,后一两位可能有“=”,生成的编码都是ascii字符.优点:速度快,ascii字符,肉眼不可理解缺点:编码比较长,非常容 ...

  9. Python和shell中Base64编码使用那些事

    做开发第一个接触的编码方式就是Base64,当时是用url来传输一些参数,传输的两端会用Base64来编码和解码,保证数据不被url转义破坏. 下面是 维基百科 Base64 中的介绍,其实自己实现起 ...

随机推荐

  1. SSM项目配置文件及其各项使用

    $说明: ·Spring 5  + Mybatis 3.4.5 +SpringMVC  ·使用druid数据库 ·使用log4j输出日志 $Spring 及其配置文件(部分) Spring官方网站:h ...

  2. SSI简介 与 nginx开启SSI

    Server Side Include : 服务器端嵌入 原理 : 将内容发送到浏览器之前,可以使用“服务器端包含 (SSI)”指令将文本.图形或应用程序信息包含到网页中.因为包含 SSI 指令的文件 ...

  3. Go环境搭建(Windows)

    下载MSI MSI地址 配置环境变量 GOPATH: 用于存放Go语言Package的目录,这个目录不能在Go的安装目录中 GOBIN: Go二进制文件存放目录,写成%GOROOT%\bin就好 PA ...

  4. ES更改参数max_result_window

    今天开发那边说翻页超过10000报错.早上来查阅官网手册,说from/size默认是10000.通过参数index.max_result_window进行控制.那么直接改这个参数即可. 1.先看看默认 ...

  5. 【ARC077F】SS kmp+打表找规律

    Description ​ 如果某个串可以由两个一样的串前后连接得到,我们就称之为"偶串".比如说"xyzxyz"和"aaaaaa"是偶串, ...

  6. O(n^2) 级别的排序算法

    o(n^2) 的排序算法.性能那么差,为什么还要学习? 首先,它是基础,千里之行,始于足下.它编码简单,容易实现,是一些简单情景的首选,它能给我们的问题一个暴力的解法,这样的解法也许不是最优的,但是它 ...

  7. 《Andrew Ng深度学习》笔记5

    深层神经网络 深层神经网络的组成如图,这里主要是深层神经网络符号的定义. 为什么要用深层神经网络,有什么好处?这里主要是分层的思想.在软件工程中,如果问题遇到困难,一般是通过“加多”一层的方法来解决, ...

  8. 【转】idea project中导入其他文件夹下的模块,可能出现java.io.FileNotFoundException: XXX.xml

    在一个project 中导入一个java 模块, 我要执行该模块的main函数 ,在main函数中有一个 FileReader(“generatorConfig.xml”) 而generatorCon ...

  9. python学习,day1:循环判断基本语句的几个代码

    # coding=utf-8 # Author: RyAn Bi count = 0 '''while True : print('count:',count) count = count + 1 i ...

  10. LeetCode109. 有序链表转换二叉搜索树

    109. 有序链表转换二叉搜索树 问题描述 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超 ...