[TOC]
### 介绍
`Base64`是一种用64个字符来表示任意二进制数据的方法。
### 使用
用记事本打开`exe、jpg、pdf`这些文件时,我们都会看到一大堆乱码,因为二进制文件包含很多无法显示和打印的字符,所以,如果要让记事本这样的文本处理软件能处理二进制数据,就需要一个二进制到字符串的转换方法。Base64是一种最常见的二进制编码方法。
`Base64`的原理很简单,首先,准备一个包含`64`个字符的数组:
~~~
['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']
~~~
然后,对二进制数据进行处理,每3个字节一组,一共是`3x8=24bit`,划为`4`组,每组正好`6`个`bit`:
![image](http://www.liaoxuefeng.com/files/attachments/001399415038305edba53df7d784a7fa76c6b7f6526873b000)
~~~
base64-encode
~~~
这样我们得到`4`个数字作为索引,然后查表,获得相应的`4`个字符,就是编码后的字符串。
所以,`Base64`编码会把`3`字节的二进制数据编码为`4`字节的文本数据,长度增加`33%`,好处是编码后的文本数据可以在邮件正文、网页等直接显示。
如果要编码的二进制数据不是`3`的倍数,最后会剩下`1`个或`2`个字节怎么办?`Base64`用`\x00`字节在末尾补足后,再在编码的末尾加上`1`个或`2`个`=`号,表示补了多少字节,解码的时候,会自动去掉。
Python内置的`base64`可以直接进行`base64`的编解码:
~~~
>>> import base64
>>> base64.b64encode(b'binary\x00string')
b'YmluYXJ5AHN0cmluZw=='
>>> base64.b64decode(b'YmluYXJ5AHN0cmluZw==')
b'binary\x00string'
~~~
由于标准的`Base64`编码后可能出现字符`+`和`/`,在`URL`中就不能直接作为参数,所以又有一种`"url safe"的base64`编码,其实就是把字符`+`和`/`分别变成`-`和`_`:
~~~
>>> base64.b64encode(b'i\xb7\x1d\xfb\xef\xff')
b'abcd++//'
>>> base64.urlsafe_b64encode(b'i\xb7\x1d\xfb\xef\xff')
b'abcd--__'
>>> base64.urlsafe_b64decode('abcd--__')
b'i\xb7\x1d\xfb\xef\xff'
~~~
还可以自己定义`64`个字符的排列顺序,这样就可以自定义`Base64`编码,不过,通常情况下完全没有必要。
`Base64`是一种通过查表的编码方法,不能用于加密,即使使用自定义的编码表也不行。
`Base64`适用于小段内容的编码,比如数字证书签名、`Cookie`的内容等。
由于`=`字符也可能出现在`Base64`编码中,但`=`用在`URL、Cookie`里面会造成歧义,所以,很多`Base64`编码后会把`=`去掉:
~~~
# 标准Base64:
'abcd' -> 'YWJjZA=='
# 自动去掉=:
'abcd' -> 'YWJjZA'
~~~
去掉`=`后怎么解码呢?因为`Base64`是把`3`个字节变为`4`个字节,所以,`Base64`编码的长度永远是`4`的倍数,因此,需要加上`=`把`Base64`字符串的长度变为`4`的倍数,就可以正常解码了。
### 小结
`Base64`是一种任意二进制到文本字符串的编码方法,常用于在`URL、Cookie`、网页中传输少量二进制数据。
### 练习
请写一个能处理去掉`=`的`base64`解码函数:
~~~
# -*- coding: utf-8 -*-
import base64
def safe_base64_decode(s):
pass
# 测试:
assert b'abcd' == safe_base64_decode(b'YWJjZA=='), safe_base64_decode('YWJjZA==')
assert b'abcd' == safe_base64_decode(b'YWJjZA'), safe_base64_decode('YWJjZA')
print('Pass')
~~~
- Python教程
- Python简介
- 安装Python
- Python解释器
- 第一个 Python 程序
- 使用文本编辑器
- Python代码运行助手
- 输入和输出
- 源码
- learning.py
- Python基础
- 数据类型和变量
- 字符串和编码
- 使用list和tuple
- 条件判断
- 循环
- 使用dict和set
- 函数
- 调用函数
- 定义函数
- 函数的参数
- 递归函数
- 高级特性
- 切片
- 迭代
- 列表生成式
- 生成器
- 迭代器
- 函数式编程
- 高阶函数
- map/reduce
- filter
- sorted
- 返回函数
- 匿名函数
- 装饰器
- 偏函数
- Python函数式编程——偏函数(来自博客)
- 模块
- 使用模块
- 安装第三方模块
- 面向对象编程
- 类和实例
- 访问限制
- 继承和多态
- 获取对象信息
- 实例属性和类属性
- 面向对象高级编程
- 使用__slots__
- 使用@property
- 多重继承
- 定制类
- 使用枚举类
- 使用元类
- 错误、调试和测试
- 错误处理
- 调试
- 单元测试
- 文档测试
- IO编程
- 文件读写
- StringIO和BytesIO
- 操作文件和目录
- 序列化
- 进程和线程
- 多进程
- 多线程
- ThreadLocal
- 进程 vs. 线程
- 分布式进程
- 正则表达式
- 常用内建模块
- datetime
- collections
- base64
- struct
- hashlib
- itertools
- contextlib
- XML
- HTMLParser
- urllib
- 常用第三方模块
- PIL
- virtualenv
- 图形界面
- 网络编程
- TCP/IP简介
- TCP编程
- UDP编程
- 电子邮件
- SMTP发送邮件
- POP3收取邮件
- 访问数据库
- 使用SQLite
- 使用MySQL
- 使用SQLAlchemy
- Web开发
- HTTP协议简介
- HTML简介
- WSGI接口
- 使用Web框架
- 使用模板
- 异步IO
- 协程
- asyncio
- async/await
- aiohttp
- 实战
- Day 1 - 搭建开发环境
- Day 2 - 编写Web App骨架
- Day 3 - 编写ORM
- Day 4 - 编写Model
- Day 5 - 编写Web框架
- Day 6 - 编写配置文件
- Day 7 - 编写MVC
- Day 8 - 构建前端
- Day 9 - 编写API
- Day 10 - 用户注册和登录
- Day 11 - 编写日志创建页
- Day 12 - 编写日志列表页
- Day 13 - 提升开发效率
- Day 14 - 完成Web App
- Day 15 - 部署Web App
- Day 16 - 编写移动App
- FAQ
- 期末总结