1. 程式人生 > 實用技巧 >base64編碼

base64編碼

二進位制的資料,每個字元的取值範圍都是[0, 255],作為ascii碼解析時,只有部分可列印。

比如,我用文字編輯器vim開啟一張jpeg圖片,會發現內容是亂碼。以下是頭兩行資料:

ÿØÿà^@^PJFIF^@^A^A^@^@^A^@^A^@^@ÿÛ^@C^@^F^D^E^F^E^D^F^F^E^F^G^G^F^H
^P

我在vim下輸入:%!xxd,可以檢視二進位制資料對應的十六進值。以下是頭兩行資料:

00000000: ffd8 ffe0 0010 4a46 4946 0001 0100 0001  ......JFIF......
00000010: 0001 0000 ffdb 0043 0006 0405 0605 0406  .......C........

注意,下面的兩行和上面的兩行內容長度不一定是相同的。上面的資料是遇到ascii碼為換行時換行,下面的資料是每16個位元組換行。

比較上下兩份資料,可以看到JFIF在上面也列印了,在下面的右半部分也列印了。說明他們的資料來源確實是同一份,只是展示方式不同。

顯然,如果我想肉眼看這份二進位制資料,或者說作為文字拷貝這份資料,16進位制的格式要優於二進位制ascii碼格式。

但是,16進製表示法,需要兩個位元組才能表示表示原始資料的一個位元組。比如4a464946表示JFIF。即大小增加了一倍。

有點大?於是,有人發明了base64演算法。它保持了編碼後可列印特性的同時,大小隻增加1/3。

base64演算法原理

base64將原始二進位制資料每三個位元組(也即24位)看成一個單元,然後將24位按每6位進行一次切割,切割成4個位元組。切割後每個位元組的範圍是[0, 63]。

由於[0, 63]的ascii碼也並不都是可列印的,於是將[0, 63]再一一對應換算成一個可列印的字元。

標準文件RFC 4648對該對映關係定義如下:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

也即0對應A,1對應B,63對應/。

換算之後,三個位元組就變成可列印的四個位元組的內容了。

解碼方的邏輯,先將每個可列印的字元按剛才的對映表反算出對應的值。然後每4個位元組看成一個單位,按位運算還原出原始的3個位元組。比如:解碼前第1個位元組的低6位作為解碼後第1個位元組的高6位,解碼前第2個位元組的低6位的前2位作為解碼後第1個位元組的低2位,這樣就得到了解碼後的第1個位元組。後續依次按順序推算。

還有一個問題,原始資料長度如果不是3的倍數怎麼辦?

那麼無非是兩種情況,一種是最後剩1個位元組,另一種是剩兩個位元組:

==
=

即base64保證了編碼後的字串長度為4的倍數。

廣州vi設計http://www.maiqicn.com 我的007辦公資源網站https://www.wode007.com

作為url引數時有什麼問題

有的人會把二進位制資料用base64編碼後,放入url的引數中,這麼做有一個問題,base64編碼後可能會出現+/=三個字元,而這三個字元會影響到整個url串的解析。

舉個例子,url串https://pengrl.com/all?key=value,如果將其中的value設定成1/2=,則url串變成https://pengrl.com/all?key=1/2=。其中的/和=是不是有點傻傻分不清楚呢?

那麼如何解決呢?

編碼後的+和/兩個符號來源於上面給出的那張對映表。於是標準文件RFC 4648中給出另一張對映表,將其中的+替換成了中劃線-,/替換成了下劃線_。

這裡額外說一句,base64並不適合用來做文字加密,因為演算法是公開的,並且它只是一種簡單的查表對映,有經驗的web開發者,甚至看到末尾的=都能猜到是base64編碼。即使編碼和解碼都使用自定義的對映表,根據文字規律,也很容易破解出對映表。

剩下=,上面也說過,是由於原始資料長度不是3的倍數填充得到的,解決方法也很簡單,編碼時不填充,解碼時剩餘的不夠4位元組的資料按順序解就好。

上面的兩種做法,都需要保證,編碼端和解碼端是能對上號的。

base64主要還是對二進位制做可列印編碼,如果是處理url引數,最好還是使用urlencode。urlencode是啥,下回再聊。