電腦編碼規則之:Base64編碼
簡介
我們知道電腦中的文件可以分為兩種,一種是人肉眼可讀的文本類文件,一種是肉眼不可讀的二進位文件。一般來說二進位文件如果用文本編輯器打開的話會顯示亂碼,並且二進位文件和文本文件的存儲和傳輸方式是不一樣的,那麼有沒有什麼辦法將二進位文件轉換成為文本文件進行傳輸或者存儲呢?答案是肯定的。
這種編碼方式就是我們今天要講到的Base64編碼。
Base64和它的編碼原理
Base64是一種將二進位編碼格式轉換為text編碼的一種形式。我們知道二進位編碼是0和1的形式,它的單位通常是一個位元組,也就是8bits,每個bit表示的是0或者1。
而文本編碼的格式有很多種,最早也就是最簡單的編碼格式就是ASCII編碼,ASCII編碼的全稱是American Standard Code for Information Interchange,也就是美國資訊交換標準程式碼,它主要表示的是常用的一些西歐字元。
ASCII的編碼範圍是0x00-0x7F,用十進位來表示就是0-127,總共128個字元,剛好是7bits表示的範圍。
ASCII編碼中包含了33個控制字元和95個可列印的字元,如下所示:
ASCII碼 | 含義 | ASCII碼 | 含義 | ||||
---|---|---|---|---|---|---|---|
16進位 | 10進位 | 2進位 | 16進位 | 10進位 | 2進位 | ||
0x00 | 0 | 0 | NUL 空 | 0x40 | 64 | 1000000 | @ |
0x01 | 1 | 1 | SOH 標題開始 | 0x41 | 65 | 1000001 | A |
0x02 | 2 | 10 | STX 正文開始 | 0x42 | 66 | 1000010 | B |
0x03 | 3 | 11 | ETX 正文結束 | 0x43 | 67 | 1000011 | C |
0x04 | 4 | 100 | EOT 傳輸結束 | 0x44 | 68 | 1000100 | D |
0x05 | 5 | 101 | ENQ 詢問字元 | 0x45 | 69 | 1000101 | E |
0x06 | 6 | 110 | ACK 承認 | 0x46 | 70 | 1000110 | F |
0x07 | 7 | 111 | BEL 報警 | 0x47 | 71 | 1000111 | G |
0x08 | 8 | 1000 | BS 退一格 | 0x48 | 72 | 1001000 | H |
0x09 | 9 | 1001 | HT 橫向製表 | 0x49 | 73 | 1001001 | I |
0x0A | 10 | 1010 | LF 換行 | 0x4A | 74 | 1001010 | J |
0x0B | 11 | 1011 | VT 垂直製表 | 0x4B | 75 | 1001011 | K |
0x0C | 12 | 1100 | FF 走紙控制 | 0x4C | 76 | 1001100 | L |
0x0D | 13 | 1101 | CR 回車 | 0x4D | 77 | 1001101 | M |
0x0E | 14 | 1110 | SO 移位輸出 | 0x4E | 78 | 1001110 | N |
0x0F | 15 | 1111 | SI 移位輸入 | 0x4F | 79 | 1001111 | O |
0x10 | 16 | 10000 | DLE 數據鏈路轉義 | 0x50 | 80 | 1010000 | P |
0x11 | 17 | 10001 | DC1 設備控制1 | 0x51 | 81 | 1010001 | Q |
0x12 | 18 | 10010 | DC2 設備控制2 | 0x52 | 82 | 1010010 | R |
0x13 | 19 | 10011 | DC3 設備控制3 | 0x53 | 83 | 1010011 | S |
0x14 | 20 | 10100 | DC4 設備控制4 | 0x54 | 84 | 1010100 | T |
0x15 | 21 | 10101 | NAK 否定 | 0x55 | 85 | 1010101 | U |
0x16 | 22 | 10110 | SYN 空轉同步 | 0x56 | 86 | 1010110 | V |
0x17 | 23 | 10111 | ETB 資訊組傳送結束 | 0x57 | 87 | 1010111 | W |
0x18 | 24 | 11000 | CAN 作廢 | 0x58 | 88 | 1011000 | X |
0x19 | 25 | 11001 | EM 紙盡 | 0x59 | 89 | 1011001 | Y |
0x1A | 26 | 11010 | SUB 換置 | 0x5A | 90 | 1011010 | Z |
0x1B | 27 | 11011 | ESC 換碼 | 0x5B | 91 | 1011011 | [ |
0x1C | 28 | 11100 | FS 文字分隔符 | 0x5C | 92 | 1011100 | \ |
0x1D | 29 | 11101 | GS 組分隔符 | 0x5D | 93 | 1011101 | ] |
0x1E | 30 | 11110 | RS 記錄分隔符 | 0x5E | 94 | 1011110 | ^ |
0x1F | 31 | 11111 | US 單元分隔符 | 0x5F | 95 | 1011111 | _ |
0x20 | 32 | 100000 | (space) | 0x60 | 96 | 1100000 | ` |
0x21 | 33 | 100001 | ! | 0x61 | 97 | 1100001 | a |
0x22 | 34 | 100010 | 」 | 0x62 | 98 | 1100010 | b |
0x23 | 35 | 100011 | # | 0x63 | 99 | 1100011 | c |
0x24 | 36 | 100100 | $ | 0x64 | 100 | 1100100 | d |
0x25 | 37 | 100101 | % | 0x65 | 101 | 1100101 | e |
0x26 | 38 | 100110 | & | 0x66 | 102 | 1100110 | f |
0x27 | 39 | 100111 | ‘ | 0x67 | 103 | 1100111 | g |
0x28 | 40 | 101000 | ( | 0x68 | 104 | 1101000 | h |
0x29 | 41 | 101001 | ) | 0x69 | 105 | 1101001 | i |
0x2A | 42 | 101010 | * | 0x6A | 106 | 1101010 | j |
0x2B | 43 | 101011 | + | 0x6B | 107 | 1101011 | k |
0x2C | 44 | 101100 | , | 0x6C | 108 | 1101100 | l |
0x2D | 45 | 101101 | – | 0x6D | 109 | 1101101 | m |
0x2E | 46 | 101110 | . | 0x6E | 110 | 1101110 | n |
0x2F | 47 | 101111 | / | 0x6F | 111 | 1101111 | o |
0x30 | 48 | 110000 | 0 | 0x70 | 112 | 1110000 | p |
0x31 | 49 | 110001 | 1 | 0x71 | 113 | 1110001 | q |
0x32 | 50 | 110010 | 2 | 0x72 | 114 | 1110010 | r |
0x33 | 51 | 110011 | 3 | 0x73 | 115 | 1110011 | s |
0x34 | 52 | 110100 | 4 | 0x74 | 116 | 1110100 | t |
0x35 | 53 | 110101 | 5 | 0x75 | 117 | 1110101 | u |
36 | 54 | 110110 | 6 | 0x76 | 118 | 1110110 | v |
0x37 | 55 | 110111 | 7 | 0x77 | 119 | 1110111 | w |
0x38 | 56 | 111000 | 8 | 0x78 | 120 | 1111000 | x |
0x39 | 57 | 111001 | 9 | 0x79 | 121 | 1111001 | y |
0x3A | 58 | 111010 | : | 0x7A | 122 | 1111010 | z |
0x3B | 59 | 111011 | ; | 0x7B | 123 | 1111011 | { |
0x3C | 60 | 111100 | < | 0x7C | 124 | 1111100 | | |
0x3D | 61 | 111101 | = | 0x7D | 125 | 1111101 | } |
0x3E | 62 | 111110 | > | 0x7E | 126 | 1111110 | ~ |
0x3F | 63 | 111111 | ? | 0x7F | 127 | 1111111 | DEL 刪除 |
Base64就是從ASCII編碼中挑選出64個字元和二進位一個位元組8bits進行映射,這也就是Base64中64的含義。為什麼要選擇ASCII編碼呢?這是因為ASCII編碼是最早出現的編碼形式,幾乎所有的電腦應用都對其完全支援,不會出現數據傳輸過程中的內容轉換,非常的安全。
當然Base64編碼也有多種編碼形式,比如在MIME中,Base64選擇的是A-Z, a-z, 和 0-9 總共62個字元,再加上其他自選的兩個字元組成了64個編碼字元。
64個字元用二進位表示是6bits,而常用的二進位使用一個位元組來表示,也就是8bits,那麼問題來了,怎麼將8bits的二進位用6bits的Base64字元來表示呢?
很簡單,我們只需要將3個8bits連接起來,變成24bits,這樣就可以用4個Base64來表示了。
為什麼必須對二進位進行轉換呢?這是因為互聯網中的某些傳輸協議只支援某些特定的字符集,如果是其他的字符集是不支援的。比如說常用的發送電子郵件的附件。因為SMTP協議最開始設計的時候是支援7 位 ASCII 字元,所以如果要傳輸文件的話,我們需要對文件進行編碼之後再進行傳輸。
另外Base64的一種用法就是在HTML中將圖片嵌入到網頁中,從而實現圖片的展示。
雖然Base64很好用,但是因為其只能使用6bits的字元映射集,所以會造成數據映射的損失,從而導致二進位文件編碼過後文件體積變大的缺點。
Base64的變體
Base64簡單點說就是bit到bit之間的映射,那麼肯定不止一種映射方式,我們來看下Base64編碼方式的各種變體,通常來說前62位基本上是一樣的,不同之處在後面兩個字元,以及用於填充的字元(這在某些協議中可能是強制性的,或者在其他協議中可能被刪除)。
下表是常見的Base64編碼的變體:
編碼名稱 | 編碼字元 | 編碼字元 | 編碼字元 |
---|---|---|---|
第62位 | 第63位 | 補全符 | |
RFC 1421: Base64 for Privacy-Enhanced Mail (deprecated) | + |
/ |
= mandatory |
RFC 2045: Base64 transfer encoding for MIME | + |
/ |
= mandatory |
RFC 2152: Base64 for UTF-7 | + |
/ |
No |
RFC 3501: Base64 encoding for IMAP mailbox names | + |
, |
No |
RFC 4648: base64 (standard) | + |
/ |
= optional |
RFC 4648: base64url (URL- and filename-safe standard) | - |
_ |
= optional |
RFC 4880: Radix-64 for OpenPGP | + |
/ |
= mandatory |
Base64的編碼細節
上一節我們講到了Base64編碼的基本原則和一些常見的變體,那麼到底是如何進行映射的呢?
本節我們會以Base64的標準形式RFC 4648為例來進行詳細的講解。
RFC 4648選擇+和/這兩個字元作為編碼中的第62位和63位,並且選擇=作為補全字元。
首先來觀察一下RFC 4648的映射表:
索引 | 二進位 | 字元 | 索引 | 二進位 | Char | 索引 | 二進位 | Char | 索引 | 二進位 | Char |
0 | 000000 | A |
16 | 010000 | Q |
32 | 100000 | g |
48 | 110000 | w |
1 | 000001 | B |
17 | 010001 | R |
33 | 100001 | h |
49 | 110001 | x |
2 | 000010 | C |
18 | 010010 | S |
34 | 100010 | i |
50 | 110010 | y |
3 | 000011 | D |
19 | 010011 | T |
35 | 100011 | j |
51 | 110011 | z |
4 | 000100 | E |
20 | 010100 | U |
36 | 100100 | k |
52 | 110100 | 0 |
5 | 000101 | F |
21 | 010101 | V |
37 | 100101 | l |
53 | 110101 | 1 |
6 | 000110 | G |
22 | 010110 | W |
38 | 100110 | m |
54 | 110110 | 2 |
7 | 000111 | H |
23 | 010111 | X |
39 | 100111 | n |
55 | 110111 | 3 |
8 | 001000 | I |
24 | 011000 | Y |
40 | 101000 | o |
56 | 111000 | 4 |
9 | 001001 | J |
25 | 011001 | Z |
41 | 101001 | p |
57 | 111001 | 5 |
10 | 001010 | K |
26 | 011010 | a |
42 | 101010 | q |
58 | 111010 | 6 |
11 | 001011 | L |
27 | 011011 | b |
43 | 101011 | r |
59 | 111011 | 7 |
12 | 001100 | M |
28 | 011100 | c |
44 | 101100 | s |
60 | 111100 | 8 |
13 | 001101 | N |
29 | 011101 | d |
45 | 101101 | t |
61 | 111101 | 9 |
14 | 001110 | O |
30 | 011110 | e |
46 | 101110 | u |
62 | 111110 | + |
15 | 001111 | P |
31 | 011111 | f |
47 | 101111 | v |
63 | 111111 | / |
補全符 | = |
我們來以單詞man為例,來觀察一下Base64的編碼流程。
man這個單詞在ASCII中分別用77, 97和110表示,轉換成為二進位就是01001101, 01100001 和 01101110。
將上面的三個二進位合併在一起就成了:010011010110000101101110, 總共24-bit,從上面的表中選擇出對應的字元,所以我們可以得到man經過base64編碼之後得到:TWFu。
上面的例子中,man剛好是3個字元,也就是24個bits,可以用base64完整的表示。如果我們只有ma這兩個字元,應該怎麼進行編碼呢?
和上面一樣,ma的二進位分別是01001101, 01100001,合併起來就是0100110101100001。
但是上面的bits只有16位,因為一個base64是6bits,所以可以用3個base64來表示,因為原始的bits少了兩位,所以用0來補全:
0100110101100001+00 = 010011010110000100。
010011010110000100轉換成為base64就是TWE,因為base64編碼需要4個字元,所以最後的字元用=來補全,也就是說me經過base64之後變成TWE=。
總結
以上就是Base64的基本含義和轉換規則,其實協議很簡單,將要轉換的數據變成二進位,然後對照轉換表格進行轉換和補全即可。
本文已收錄於 //www.flydean.com/18-base64-encoding/
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!