一个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 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

pwnable.tw刷题之dubblesort

前言 上一篇中我介绍了phttp://www.freebuf.com/articles/others-articles/134271.htmlwnable.tw...

3006
来自专栏程序员互动联盟

【问题追踪】scanf引发的“血案”

最近我遇到C语言学习中一个比较容易让我们这种新手犯错或者忽略的地方。那就是关于scanf函数在读取字符时的问题,以及getchar()与回车的一点恩恩怨怨,换句...

2836
来自专栏用户2442861的专栏

最常用的两种C++序列化方案的使用心得(protobuf和boost serialization)

http://blog.csdn.net/lanxuezaipiao/article/details/24845625

5142
来自专栏java达人

把好方法参数的大门

做编程工作这几年来,见识了不少烂代码,最常见的就是像下面那样的: public void execute(Args args){ //方法体内对args没有作...

1937
来自专栏熊二哥

GOF设计模式快速学习

这段时间,学习状态比较一般,空闲时基本都在打游戏,和研究如何打好游戏,终于通过戏命师烬制霸LOL,玩笑了。为了和"学习"之间的友谊小船不翻,决定对以往学习过的G...

1929
来自专栏机器学习算法与Python学习

Python再次更新! 解锁与优化多项新特性......

Python 3.7.0 版本于 6 月 27 号正式发布,该版本有多项重大的更新和改进,主要内容如下如下:

1000
来自专栏数据小魔方

R语言多任务处理与并行运算包——foreach

相信大部分R语言初学者,在刚开始入门之处,都曾被告诫在处理多重复任务时,尽量不要使用显式的for循环,而要尽可能的使用R语言内置的apply组函数,这样可以极大...

41811
来自专栏IMWeb前端团队

朋友你听说过尾递归吗

我们以斐波那契数列为例子讲解了尾递归的运用方式,并对比了普通递归与尾递归的性能。

6539
来自专栏数据小魔方

Python可视化笔记之folium交互地图

leftlet给R语言提供了很好用的交互式动态地图接口,其在Python中得API接口包名为folium(不知道包作者为何这样起名字,和leaflet已经扯不上...

3824
来自专栏JavaEdge

Java多线程中join方法的理解

3836

扫码关注云+社区