编码原理理解之「 base64」

一、什么是 base64

Base64 是网络上最常见的用于传输 8Bit 字节码的编码方式之一,是一种基于 64 个可打印字符(见下图👇)来表示 二进制数据 的方法。

标准base64编码对应表

也就是说,经过 base64 转换过后的二进制数据,其只由 65 个字符(上述 64个👆 + 末尾可能填充的”=“)组成。👇

Base64 是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。
.
== base64编码后 ==>
.
QmFzZTY0IOaYr+e9kee7nOS4iuacgOW4uOingeeahOeUqOS6juS8oOi+kzhCaXTlrZfoioLnoIHnmoTnvJbnoIHmlrnlvI/kuYvkuIDvvIxCYXNlNjTlsLHmmK/kuIDnp43ln7rkuo42NOS4quWPr+aJk+WNsOWtl+espuadpeihqOekuuS6jOi/m+WItuaVsOaNrueahOaWueazleOAgg==

对吧,找不到第 66 个字符。

但为什么会有 base64 这么奇怪的编码方式呢?

邮件传输协议(SMTP)是不支持不可见字符的传递的(不可见字符比如换行符、制表符等)。但是图片的数据所表示的字符中,存在大量的不可见字符,那么,邮件就不能传输图片么?显然不可接受。为了解决这个问题,大牛们提出了 base64 编码,传输时通过 base64 将任何二进制数据都用可见字符来表示。

如今,base64 也被应用在 X.509 公钥证书约定中,HTTP请求的参数编码上,XML结构中存储二进制数据的场景中。简而言之,基于数据传输、存储,屏蔽了大量的不可见字符(他们往往有特殊含义),传输,存储的可靠性大幅提升。

二、base64 的编码规则

base64是对二进制数据进行编码的!base64是对二进制数据进行编码的!base64是对二进制数据进行编码的! 此点不同于我们常见的 utf-8 编码(做「字符串」&「二进制」之间的转换),base64 做的是「二进制」 & 「能转化为可见字符的二进制」之间的转换。

简单来讲,base64 编码分为如下几步:
1)将二进制数据按照 3字节(24位)分组;
2)将每组数据分为 4份(每份6位)
3)每份数据 (6位能表示0~63) 查找 base64 编码对应表,找到对应字符;
4)将对应字符基于 ASCII 表示,最后纯空的份用 ’=‘ 替代。

三、base64 的编码举例

比如我们想对 ”今儿个真Happy!“ 的 utf8(你当然可以用其他的编码方式转换,毕竟 base64 只关心二进制输入)的二进制数据进行编码。

Step1:获取待编码的二进制数据

”今儿个真Happy!“,经过 utf-8 编码的二进制数据如下👇

今儿个真Happy!

== utf-8编码 ==>

十六进制表示:e4 bb 8a e5 84 bf e4 b8 aa e7 9c 9f 48 61 70 70 79 ef bc 81 
二进制表示:11100100 10111011 10001010 11100101 10000100 10111111 11100100 10111000 10101010 11100111 10011100 10011111 01001000 01100001 01110000 01110000 01111001 11101111 10111100 10000001

Step2:将二进制数据分组

接下来,我们需要将待编码的二进制数据按照 3字节 一组分组。因为 1字节8位base6464 个可见字符需要用 6位(2的6次方)表示,86 的最小公倍数是 24,即 3 个字节。

11100100 10111011 10001010 11100101 10000100 10111111 11100100 10111000 10101010 11100111 10011100 10011111 01001000 01100001 01110000 01110000 01111001 11101111 10111100 10000001

== 3字节一组 ==>

11100100 10111011 10001010 
11100101 10000100 10111111 
11100100 10111000 10101010 
11100111 10011100 10011111 
01001000 01100001 01110000 
01110000 01111001 11101111 
10111100 10000001 

但我们发现原始二进制的字节数无法被3整除,没关系,我们后面再处理。

Step3:每组做4等分

我们将每组数据进行 4 等分(每份 6 位),基于 6 位得到 0 ~ 63 的值,即为 base64 编码的索引。

11100100 10111011 10001010 
11100101 10000100 10111111 
11100100 10111000 10101010 
11100111 10011100 10011111 
01001000 01100001 01110000 
01110000 01111001 11101111 
10111100 10000001 

== 4等分 ==>

111001 001011 101110 001010 
111001 011000 010010 111111 
111001 001011 100010 101010 
111001 111001 110010 011111 
010010 000110 000101 110000 
011100 000111 100111 101111 
101111 001000 0001 

最后一行非纯空的数据补 0 补满 6 位,纯空数据当做 '=':
101111 001000 0001  ==> 101111 001000 000100 '='

== 每份二进制转为10进制 ==》

05 11 20 10 
05 24 18 63 
05 11 08 16 
05 05 24 05 
18 06 05 22 
02 07 13 21 
21 08 04 ’=‘

Step4:做Base64的字符映射

05 11 20 10 
05 24 18 63 
05 11 08 16 
05 05 24 05 
18 06 05 22 
02 07 13 21 
21 08 04 ’=‘

== base64 字符映射 ==>

5 L u K 
5 Y S /
5 L I q
5 5 y f
S G F w
c H n v
v I E = 

对应 [ASCII](https://baike.baidu.com/item/ASCII/309296?fr=aladdin) 的二进制数据即:

00110101 01001100 01110101 01001011 
00110101 01011001 01010011 00101111 
00110101 01001100 01101001 01110001 
00110101 00110101 01111001 01100110 
01010011 01000111 01000110 01110111 
01100011 01001000 01101110 01110110 
01110110 01001001 01000101 00111101 

这即为base64完成编码后在内存中的值👆

四、讨论

4.1 网上常见的base64算法过程的补两个0问题

网上很多关于 base64 的说明教程(包括百度百科)都描述了 3字节(3x8位)4x6位 后,在6位 数据前 补两个’0‘凑4组8位数据 的问题。而这个步骤存在的意义实质上在于方便 base64 标准代码实现的理解。在此,我们不分析 base64 编码的代码实现,所以小编未将该步骤加入其中。

百度百科的组转换示例

4.2 base64的索引存在与设计层,其存储层使用的是字符编码。

第二个很让人困惑的问题就是 base64 转换后的二进制数据,如果你将 base64 转换的二进制数据打印出来,会发现每个字节的数据并不仅仅在 0 ~ 63 这个范围内。

这是因为,base64 的实际存储形式并非其索引值……

ABCD base64的映射

比如一份转化后的 base64 数据映射到 ABCD,则其二进制表示并不是0x01020304,而是 ABCD 对应ASCII标中的值:0x41424344

ABCD ASCII的映射

base64 的设计是不关心转换的 base64 字符是如何存储的。但实际应用中,ASCII,或讲完全兼容 ASCIIutf-8 实在是太通用了,所以我们直接将 base64 与其结合,其实无可厚非。无论是苹果官方的 base64 编码,还是大家用得比较多的也比较老牌的 GTMBase64,都是这么做的。

iOS自带base64编码接口说明

4.3 base64的变种

即便 base64 只使用了除等号 ”=“ 之外的64个字符,但它还是不出意外地会在各种场景下出问题……关键在于 62号 字符 "+",和 63号 字符 "/",它们在某些场景下是有特殊含义的。所以,我们也会在必要的场景将 ”+“”/“ 两个字符替换,组成变种的 base64 编码,比如:

变种1:URL变种:"+","/","=" --> "-","_",""

URL编码器会把标准 Base64 中的 “/”“+” 字符变为形如 “%XX” 的形式,而这些 “%” 号在存入数据库时还需要再进行转换,因为 ANSI SQL 中已将 “%” 号用作通配符。为解决此问题,可采用一种用于 URL 的改进 Base64 编码,它不仅在末尾去掉填充的 '=' 号,并将标准 Base64 中的 “+”“/” 分别改成了 “-”“_”,这样就免去了在 URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。

变种2:正则变种:"+","/" --> "!","-"
另有一种用于正则表达式的改进 Base64 变种,它将 “+”“/” 改成了 “!”“-”,因为 “+” , “/”在正则表达式中具有特殊含义。

五、实践

用一行代码来打印 base64 的编码流程吧~

/* 代码示例 */
cytLogStringData_base64_flow(@"今儿个真Happy!");
/* 打印流程 */
1 输入字符串: 
今儿个真Happy!

2 将字符串转为数据(通过UTF-8)编码:
16进制数据表示: e4 bb 8a e5 84 bf e4 b8 aa e7 9c 9f 48 61 70 70 79 ef bc 81 
2进制数据表示: 11100100 10111011 10001010 11100101 10000100 10111111 11100100 10111000 10101010 11100111 10011100 10011111 01001000 01100001 01110000 01110000 01111001 11101111 10111100 10000001 

3 将2进制数据进行3字节分组:
11100100 10111011 10001010 
11100101 10000100 10111111 
11100100 10111000 10101010 
11100111 10011100 10011111 
01001000 01100001 01110000 
01110000 01111001 11101111 
10111100 10000001 

4 将2进制数据每3个字节分为4组:
111001 001011 101110 001010 
111001 011000 010010 111111 
111001 001011 100010 101010 
111001 111001 110010 011111 
010010 000110 000101 110000 
011100 000111 100111 101111 
101111 001000 0001

5 获取base64编码的索引值:
57 11 46 10 57 24 18 63 57 11 34 42 57 57 50 31 18 06 05 48 28 07 39 47 47 08 04 00 

6 将索引映射为base64目标字符:
5LuK5YS/5Liq55yfSGFwcHnvvIE=

7 base64字符串内存中的样子(对应字符的ASCII值,或说utf-8编码值):
00110101 01001100 01110101 01001011 00110101 01011001 01010011 00101111 00110101 01001100 01101001 01110001 00110101 00110101 01111001 01100110 01010011 01000111 01000110 01110111 01100011 01001000 01101110 01110110 01110110 01001001 01000101 00111101 

git地址:https://github.com/chrisYooh/CYEncoding.git
对你有帮助的话记得帮小编点个 「Star」 哦!✨✨✨

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335