一个bit一个bit的进行 Base64 白话科普,看不懂算你输

大家好,湿兄又来吹牛逼了 因为最近需要将任意格式、任意大小的文件进行 Base64 编码存储,所以把 Base64 编解码撸一遍。 总是先有需求,再有市场嘛~

写在前面

首先,让人放心的是,Base64 没什么难的。

其次,让人放心的是,看完 Base64 编解码算法后,实现任意文件编解码也没啥难的。

所以,你输的可能性不大~

Base64 是什么?

一种「编码方式」。

一种用「可读字符」来表示「二进制数据」的编码方式。

对比使用一下平时将exe文件用记事本打开的骚操作,你就明白啥叫可读字符了。Six不Six?

使用 Base64,你可以将任意数据或文件以「可读字符形式发送或存储」。参考维基百科可以看到这一段:

Base64是一种基于64个可打印字符来表示二进制数据的表示方法

所以Base64为什么叫Base64而不是叫Base32、Base100,是因为它是用64个可打印字符来表示二进制数据的。

通常,会有人误认为 Base64 是一种加密方式,这是错误的。虽然进行 Base64 编码后的数据会显得杂乱无章、看不出原数据,但是对于带有恶意的人,Base64 无法阻止他们的恶意。所以想加密,还请用正了八经的加密算法。

Base64 编解码算法

Base64 的算法可以说是「按图索骥」。

这个表划重点,后面要考 表示范围: A-Z,a-z,0-9,+,/ 全部内容:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

讲之前,咱们要知道:1Byte(字节) = 8bit(比特)。好,开始。

Base64 算法:

  1. 以原数据中每 6 个 bit 作为一个单位,在取出的 6bit 前方填充 2bit 的 0 后,变为 8bit,也就是一字节。设最后剩余bit数为 t (t <= 6),则补充的 bit 0 个数为 8 - t 个。
  2. 将每个填充过 bit 0 的单位转化为对应数字。每个数字值的范围为 0 - 63(2的6次方 - 1 = 00111111)。
  3. 将数字作为下标,在表中查找其对应表示的字符。

注意事项:

  1. 用 Base64 的方法取数据(每次取6bit),那么想取“完整字节数”,最少需要4次,因为 6 和 8 的最小公倍数是 24,也就是 3 字节 == 4个 Base64 单位。
  2. 对于不足 3 的倍数字节的原数据,作以下处理:
    1. 余数为 2,也就是差了一个字节,则在最后结果补一个「 = 」
    2. 余数为 1,则在最后的结果补两个「 = 」

防止有的同学没看明白,这里用猿湿Xoong的「Xoong」作为原数据,给大家展示下 Base64 的编码过程。

原数据:

Xoong 5字节

ASCII 编码后的二进制数据为:

X -> 01011000 o -> 01101111 o -> 01101111 n -> 01101110 g -> 01100111

Base64 编码过程(每次取6bit)

原数据以 6bit 为单位分解 010110 000110 111101 101111 011011 100110 0111 编码后,共7个字节,前方加粗的 bit 0 是填充的 00010110 00000110 00111101 00101111 00011011 00100110 00000111 对应数字为 22 6 61 47 27 38 7

下列为「Xoong」最终的 Base64 encode 后的数据。因为「原数据」缺少了 1 个字节才到 6 字节,所以加了个「 = 」

对应最开头划重点的表中字符,结果为 WG9vbmc=

细心的同学会发现,编码后的数据长度变长了。是的,基于算法特性,经过 Base64 编码的数据长度会增加 1/3,也就是原来的 4/3 倍(6bit 变 8bit 嘛)。

简单吧,没骗你们吧?

对文件进行 Base64 编码

什么?有人说我上一节没讲解码?你们把编码流程倒过去就是解码了,完全可逆。

对于文件的读,我们永远不能忽视一个客观事实:文件的size有时比内存总量还大。

100G 的文件对 8G 的内存

这时就会存在:直接读取大文件的全部数据将会导致系统 OOM。有经验的同学一定会边坐着小板凳边嗑着瓜子的说:“分段读取不就行了?”,大佬,大佬!这位同学说的连一个标点符号都没错,「分段读取」。

但是,BUT

分段读取之后,就会面临着分段进行 Base64 编码的情况。由于 Base64 算法的第一点,如果不注意每次分段读取的数据量,就会导致数据失真。

还以上面的“Xoong”为例,假设A文件存储此字符串,程序分别以每次1、2、3个字节读取,并最后存储在B文件中。看看最终的到的结果。

每次读取1字节

X 010110 00 编码后 00010110 00000000 结果 WA== 其余字符:o -> bw==, n -> bg==, g - > Zw==


依次写入文件B,最终文件B数据:WA==bw==bw==bg==Zw==,对比正确数据:WG9vbmc=,失真

每次读取2字节

Xo 010110 000110 1111 编码后 00010110 00000110 00001111 结果 WG8= 其余字符:on -> b24=, g -> Zw==


依次写入文件B,最终文件B数据:WG8=b24=Zw==,对比正确数据:WG9vbmc=,失真

每次读取3字节

Xoo 010110 000110 111101 101111 编码后,无冗余bit 00010110 00000110 00111101 00101111 结果 WG9 其余字符:ng -> vbmc=


依次写入文件B,最终文件B数据:WG9vbmc=,对比正确数据:WG9vbmc=,正确

上面的过程证明了:如果不是以「3的倍数字节」进行文件数据分段读取,将会造成「数据失真」。

综上,我们在对文件进行 Base64 编码的时候,需要注意两点:

  1. 进行分段读取
  2. 每次读取3的倍数个字节的数据,如 3 * 1024

对文件进行 Base64 解码

忽然就懒了,不想写对 Base64 进行解码的时候需要注意什么了,大家自己研究研究吧。提个醒,「怎么来的,就怎么回去」。

我已经用Python实现了一个完整的包含文件与 Base64 之间正反编码的脚本,需要的同学后台回复「 base64 」获取。

写在最后

大家可以看得出来,这篇文章了里的例子,是湿兄「一个bit一个bit」打出来的。关注分享点赞留言,还不来个一条龙嘛?

想到最近的「洗稿」实锤,我就想说一句,谁要是有耐心把我这篇洗了,那你就标原创吧~

哈哈。

原文发布于微信公众号 - 猿湿Xoong(skypeng-funny)

原文发表时间:2018-05-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java技术栈

Java架构师必须知道的 6 大设计原则

原文:www.cnblogs.com/pengdai/p/9151800.html

703
来自专栏吉浦迅科技

DAY36:阅读”执行空间&扩展修饰符

1123
来自专栏walterlv - 吕毅的博客

.NET Core/Framework 创建委托以大幅度提高反射调用的性能

发布于 2018-02-07 09:45 更新于 2018-02...

421
来自专栏Vamei实验室

Python小题目 针对快速教程

作业的目的是帮助熟悉之前学习的内容:  1. 写一个程序,判断2008年是否是闰年。 写一个程序,用于计算2008年10月1日是这一年的第几天?(2008年1月...

1945
来自专栏技术专栏

慕课网Flask高级编程实战-6.书籍详情页面的构建

大多时候,我们从数据库,或者外部网络获取到的原始数据,并不能满足复杂的业务需求。业务的直观体现就是页面。

771
来自专栏Golang语言社区

Go的语言特性总结

写在前面: 近来关于对Golang的讨论有很多,七牛的几个大牛们也断定Go语言在未来将会快速发展,并且很可能会取代Java成为互联网时代最受欢迎的编程语言。G...

3967
来自专栏游戏杂谈

php正则表达式的分组捕获

经过测试,发现php正则表达式获取分组捕获是从$0开始,而平时工作中JavaScript中的正则是$1..$9

333
来自专栏java一日一条

由字符串反转(使用递归)引申出来一道Java面试题

如何面试一个从事编程工作的开发人员既困难又乏味,幸好还有很多值得参考的指南,比如:《Joel Guerilla Guide to interviewing》,...

712
来自专栏数据科学学习手札

(数据科学学习手札31)基于Python的网络数据采集(初级篇)

  在实际的业务中,我们手头的数据往往难以满足需求,这时我们就需要利用互联网上的资源来获取更多的补充数据,但是很多情况下,有价值的数据往往是没有提供源文件的直接...

43513
来自专栏小文博客

小文’s blog — 奇妙的数字 –《蓝桥杯代码笔记2》

1035

扫码关注云+社区