base-122編碼,比base-64更加高效

這是一種比base-64更加節省空間的編碼方式。github地址

1.概述

在網絡編程中,我們常需要利用一些文本傳輸協議(如POP3SMTP)協議來傳輸二進制數據。

這時候必須要先將它們轉化爲文本格式才能進行傳輸,最常用的編碼方式就是base-64。它雖然簡單方便,容易實現,但是將會帶來33%的數據量增長。

2.一個小例子

有些時候我們想把圖片、字體、音頻等二進制文件作爲url的一部分傳輸給客戶端/服務器,這樣可以減少一次請求和響應。當然,我們必須保證這些文件不能太大。

比如,原來是這樣寫的:


<img src="example.png" />

爲了避免這次額外的資源請求,我們可以利用base-64編碼從而換成下面的寫法:

<img src="data:image/png;base64,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" />

雖然這種做法可能對於小文件來說確實可以優化整個數據傳輸的效率,但是如果不慎用於大文件將很可能導致性能的下降。原因就是base-64編碼會導致額外增加33%的傳輸量。下面講簡要分析其原因。

3.base-64編碼

base-64是將二進制數據轉換爲文本數據的最常用方法,比如說要把一個字節的二進制數據–01101001插入到一個文本文件中。最直接的方法就是找兩個字符分別與01對應,如果我們選擇的是AB,那麼轉換的結果就是下面這樣的(假設這是一個只有1個像素的圖片文件)。


<img src="data:image/png;sillyEncoding,ABBABAAB" />

雖然實現起來很簡單,但是編碼後的數據會變爲原來的八倍。

對於base-64,他會把數據按照每6個位分爲一組(一共有64種組合方式),每種組合方式(編號爲063)各對應一個字符,然後進行編碼。這樣轉換前後的數據量之比就爲8:6

如果原數據的數據量(以位爲單位計算)不是6的倍數,添加1個2個額外的字節來湊足24位,然後分成3組後再對應編碼。但是這1個2個額外添加的字節不參與編碼,而是最後統一轉換成1個或2個=號。

那麼,如果使用base-64來編碼上面的圖片文件,結果將變成:


<img src="data:image/png;base64,aQ==" />

base-64編碼使用的字符包括26個大寫字母+26個小寫字母+10個阿拉伯數字,最後還有兩個特殊符號+/

爲了改良base-64,我們可以考慮使用其他的編碼字符,但是ASCII的限制太多,很難再找到更加高效並且簡單可行的映射關係,所以我們下面嘗試在UTF-8中尋找解決方案。

4.UTF-8編碼

因爲大部分的網頁文件(尤其是有漢字的)其實是由UTF-8編碼而成的,所以我們的想法是完全可行的。

下面我們開始介紹關於UTF-8的一些核心概念。

UTF-8是一種對於Unicode的可變長字符編碼方式,一共支持1,112,064個符號點。所謂符號點,其實就是可以表示爲一個字符的數字(在Unicode中,並不是所有的數字都可以轉換表示成一個字符)。對於ASCII字符的編碼,可以只用一個字節;但是對於某些的符號點,就可能需要使用多達4個字節來表示這個字符。

Unicode中符號點的範圍 UTF-8編碼 所用的總位數 符號點所佔的位數 增加率
0x00 – 0x7F 0xxxxxxx 8 7 8:7
0x80 – 0x7FF 110xxxxx 10xxxxxx 16 11 16:11
0x800 – 0xFFFF 1110xxxx 10xxxxxx 10xxxxxx 24 16 24:16
0x10000 – 0x10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 32 21 32:21

上面的增加率,就是表示該範圍內的符號數所用的位數和實際上真正有效的位數(其他的位只是作爲輔助標識)。

如果使用上表中的單字節編碼方式來將我們的二進制數據轉換成UTF-8編碼,那麼結果將會是下面這樣子的:

每7位爲一組

如果使用這種每7位1組的編碼方式,那麼編碼之後得到的數據量將從base648:6下降爲8:7

但是很遺憾,如果在網頁中使用這種編碼方式,將會引起衝突,不然base64也不會從ASCII編碼字符只中選取其中的64個作爲目標字符,原因還是爲了避免在網頁解析時發生衝突。

比如,我們轉換後的數據一般是以下面這種形式出現在網頁代碼中的:

<img src="data:image/png;ourEncoding,(Encoded data)" />

(如果數據中出現",這個字符就可能使瀏覽器誤以爲是字符串的結束,進而造成數據截斷)。

5.如何避免衝突

爲了保證編碼後得到的字符不會在HTML頁面中引起衝突,我們需要找出所有的“不安全”的字符。(比如雙引號"

除了雙引號"以外,還有換行字符\n、回車字符\r、轉義字符\&、不可顯示的null字符0x00都會引起解析衝突。

這樣,把上面的6個”不安全“字符剔除後,只剩下122個字符。

6.base-122編碼

這樣我們的方案基本成型了,將原二進制數據按照每7位爲一組(如果數據長度不是7的倍數,則需要填充),然後直接轉換爲UTF-8中的單字節字符,0xxxxxxx

如果轉換後的字符不湊巧就是6個“不安全”的字符之一,則做進一步的轉換,將其變成雙字節的UTF-8字符:110xxxxx 10xxxxxx。因爲只有6個字符,其實只需要3個位就行了,這裏我們使用sss來表示這些實際有效的數據位,110sssxx 10xxxxxx

那麼剩下來還有8個位可以存放數據,這8個位完全足夠存放下一組的7個位的數據。這樣我們選後面的7位來存放下一組的數據即可,110sss1x 10xxxxxxsss用來標識不安全的6個字符,xxxxxxx用來存放下一組的數據)。

base-122編碼的最終效果如下:
base-122

終於,我們完成了編碼前後數據比爲7:8的目標,編碼同樣的數據,所佔用的數據量是base-6486%

7.如何使用

使用前必須保證你的HTML文件使用的是UTF-8編碼格式。

<head><meta charset="utf-8"></head>

用法見github首頁的介紹。

https://github.com/kevinAlbs/Base122

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章