对象base64转码_什么是 Base64 编码
什么是 Base64
Base64 是二進(jìn)制到字符編碼的一種方案,將二進(jìn)制數(shù)據(jù)使用 ASCII 字符串格式表示,即翻譯為基數(shù)為 64 的一種表示。每個 Base64 數(shù)字表示一個 6 比特的數(shù)據(jù)。三個字節(jié)(共 24 個比特)因此可以被表示為 4 個 Base64 數(shù)字。
Base64 常用于處理文本數(shù)據(jù)的場合,表示、傳輸、存儲一些二進(jìn)制數(shù)據(jù)。
Base64 編碼表
從 0 到 25 ,也就是從 000000 到 011001,是 ASCII 字符 A - Z
從 26 到 51,也就是從 011010 到 110011,是 ASCII 字符 a - z
從 52 到 61,也就是從 110100 到 111101,是 ASCII 字符 0 - 9
62 (111110)是 ASCII 字符 +
63 (111111)是 ASCII 字符 /
綴詞(padding) =
因為 Base64 是每 6 個比特進(jìn)行一次編碼,而現(xiàn)代電腦上的編碼值被分為了一個個 8 比特的字節(jié),因此,在 Base64 編碼的文本中,每 4 個字符表示三個字節(jié)的未編碼的文本或數(shù)據(jù)。這就意味著,當(dāng)未編碼的輸入的字節(jié)數(shù)不是 3 的倍數(shù)時,編碼輸出必須加上綴詞來使得輸出的長度是 4 的倍數(shù)。這個綴詞便是 = ,它表明:不再需要更多的比特來進(jìn)行解碼。
= 數(shù)量規(guī)則如下:若源數(shù)據(jù)的字節(jié)數(shù)是 3 的倍數(shù),則不需要加 =
若源數(shù)據(jù)的字節(jié)數(shù)除 3 余 1,則加兩個 =
若源數(shù)據(jù)的字節(jié)數(shù)除 3 余 2,則加一個 =
舉個例子來說明 = 的使用:
字符串 "Man" 的 ASCII 編碼是 01001101 ,01100001 ,01101110 ,(0x4d,0x61,0x6e)。拆成六個一組,就是 010011 ,010110 ,000101 ,101110 。對應(yīng)于 Base64 編碼表中的 T,W,F,u,故編碼后 ASCII 編碼的"Man" 對應(yīng)于 "TWFu"。
字符串 "Ma" 的 ASCII 是 01001101 ,01100001 。拆開得到 010011 ,010110 ,0001 。最后一組還少兩個比特,補零得到 000100 。對應(yīng)于 Base64 編碼得到 T,W,E,編碼結(jié)果為 "TWE="。此處的 = 表明補了兩個比特。
字符串 "M" 的 ASCII 是 01001101 ,拆開之,得到 010011 ,01 。最后一組要補四個零,得到 010000 。對應(yīng) Base64 得到 T,Q。編碼結(jié)果為 "TQ==",== 表明補了四個比特。
實際上,綴詞 = 并不是必須的,因為缺少的字節(jié)可以從編碼文本的長度中計算得到。例如 “YW55IGNhcm5hbCBwbGVhc3VyZS4” 這個編碼串共有 27 個字符,不是 4 的倍數(shù), 且由 mod(27, 4) = 1 可知它補充了兩個比特。通過這些信息就可以知道源文本(或數(shù)據(jù))(若為 ASCII 編碼)是 “any carnal pleasure.”。但是,如果多個 Base64 編碼字符串被連接在一起,= 是必要的,因為需要使用它來區(qū)分不同來源的字符串。
Base64 的解碼
當(dāng)對 Base64 進(jìn)行解碼時,四個字符通常會被轉(zhuǎn)化為三個字節(jié)。當(dāng)存在綴詞時則可能是兩個或一個,一個 = 表明四個字符會被轉(zhuǎn)化為兩個字節(jié),兩個 = 表明四字符會被轉(zhuǎn)為一個字節(jié)。上面的例子便是如此。
如果沒有綴詞,在對所有的四字符組進(jìn)行轉(zhuǎn)碼后,若還有剩下的字符,剩下的字符數(shù)一定小于四。這種情況下,只可能剩下兩個或三個字符。若剩下兩個字符,則轉(zhuǎn)為一個字節(jié),若為三個則轉(zhuǎn)為兩個字節(jié)。
一個簡單的 C 實現(xiàn)
以下代碼在 MINGW 下通過編譯并通過了下面的測試。
簡便起見,我沒用使用 stdint 頭文件,所有下面的代碼想要正確運行的話,必須滿足 int 是 32 位。
編碼
要想將一個個字節(jié)編碼為 Base64,首先需要一張二進(jìn)制碼(000000 - 111111)到 64 個字符的映射表:
char base64_encode_table[64] =
{
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};
由于現(xiàn)在的計算機中一個字節(jié)是 8 位,而 Base64 編碼中每 6 位進(jìn)行一次編碼,在處理過程中若直接使用字節(jié)(字符)數(shù)組可能不是太方便。考慮到 3 * 8 = 4 * 6,先編寫一次性對 3 個字節(jié)進(jìn)行處理的函數(shù)會方便不少,而且若最后還剩下 1 或 2 個字節(jié),可以一并處理。
void base64_convert3(char * dststr, unsigned char c1, unsigned char c2, unsigned char c3, int num)
{
// dststr must be big enough to contain four char
// num is the number of byte to convert
int convert = 0;
convert = (c1 << 16) | (c2 << 8) | c3;
for (int i = 0; i < 4; i++)
{
int temp = (convert >> (i * 6)) & 0x3f; //0x3f = 00111111
dststr[3 - i] = base64_encode_table[temp];
}
for (int i = 3; i > num; i--)
{
dststr[i] = '=';
}
}
上面的函數(shù)的三個字符參數(shù) c1, c2, c3 是順序排列的 3 個字節(jié),參數(shù) num 是處理字節(jié)的個數(shù)。如果 num 的值不為 3,那么 c2 或 c3 的值應(yīng)該設(shè)為 0 來確保得到正確的結(jié)果。
這個函數(shù)沒有對參數(shù)的合理性進(jìn)行檢驗。
接下來就可以完成編碼函數(shù)了。
int base64_encoder(char * dststr, char * srcstr, int num, int * dstlength)
{
// num is the number of byte
// dstlength is the number of encoded char, include '='
// dststr must be big enough to carry encoded char
// return non-zero means failed
if (dststr == NULL || srcstr == NULL || num <= 0 || dstlength == NULL)
{
return 1;
}
int cnt = 0;
int i = 0;
for (i = 0; i <= num - 3; i += 3)
{
base64_convert3(dststr + cnt, srcstr[i], srcstr[i + 1], srcstr[i + 2], 3);
cnt += 4;
}
if (i == num)
{
dstlength[0] = cnt;
return 0;
}
else if (i == num - 1)
{
base64_convert3(dststr + cnt, srcstr[i], 0, 0, 1);
cnt += 4;
dstlength[0] = cnt;
return 0;
}
else if (i == num - 2)
{
base64_convert3(dststr + cnt, srcstr[i], srcstr[i + 1], 0, 2);
cnt += 4;
dstlength[0] = cnt;
return 0;
}
else
{
return 1;
}
return 1;
}
該函數(shù)做了最低限度的參數(shù)檢查。dststr 代表編碼后字符串的目標(biāo)位置,srcstr 表示源字符串,num 是源字符串的長度,dstlength 指向一個整型變量,用于存儲得到的編碼的長度。
解碼
與編碼相似,解碼也需要一張映射表,不過 Base64 只規(guī)定了從二進(jìn)制到字符進(jìn)行編碼,而沒有規(guī)定字符的編碼,至于字符編碼是 ASCII 還是其他的比如 UTF-8,EBCDEC 。為了簡便起見,這里使用 ASCII。可以使用一個一個 if else 來去掉字符集依賴性,不過也太麻煩了點:
unsigned char base64_decode_table(unsigned char ch)
{
//use ascii code
if (isupper(ch))
{
return ch - 65; // 'A' is 65
}
else if (islower(ch))
{
return ch - 97 + 26; // 'a' is 97
}
else if (isdigit(ch))
{
return ch - 48 + 52; // '0' is 48
}
else if (ch == '+')
{
return 62;
}
else if (ch == '/')
{
return 63;
}
else
{
return 255;
}
}
同樣,與編碼類似,解碼時也可以考慮將四個字符看成一組,這樣處理帶 = 時更方便:
void base64_convert4(char * dststr, unsigned char c1, unsigned char c2, unsigned char c3, unsigned char c4, int num)
{
// num is the number of char
// dststr must not be null
int convert = 0;
convert = base64_decode_table(c4);
convert = convert | (base64_decode_table(c3) << 6);
convert = convert | (base64_decode_table(c2) << 12);
convert = convert | (base64_decode_table(c1) << 18);
for (int i = 0; i <= num - 2; i++)
{
dststr[i] = convert >> (8 * (2 - i)) & 0xff;
}
}
接下來就可以編寫解碼函數(shù)了,因為要處理使用 = 和不使用 = 的情況,代碼的選擇支有點多,看起來有點長:
int base64_decoder(char * dststr, char * srcstr, int num, int *dstlength)
{
//the min value of num is 2
if (dststr == NULL || srcstr == NULL || num < 2 || dstlength == NULL)
return 1;
int i;
int cnt = 0;
for (i = 0; i <= num - 8; i += 4)
{
base64_convert4(dststr + cnt, srcstr[i], srcstr[i + 1], srcstr[i + 2], srcstr[i + 3], 4);
cnt += 3;
}
if (i == num - 4)
{
if (srcstr[num - 1] != '=')
{
base64_convert4(dststr + cnt, srcstr[i], srcstr[i + 1], srcstr[i + 2], srcstr[i + 3], 4);
cnt += 3;
dstlength[0] = cnt;
return 0;
}
else
{
if (srcstr[num - 2] == '=')
{
base64_convert4(dststr + cnt, srcstr[i], srcstr[i + 1], 0, 0, 2);
cnt += 1;
dstlength[0] = cnt;
return 0;
}
else
{
base64_convert4(dststr + cnt, srcstr[i], srcstr[i + 1], srcstr[i + 2], 0, 3);
cnt += 2;
dstlength[0] = cnt;
return 0;
}
}
}
else if (i == num - 3)
{
base64_convert4(dststr + cnt, srcstr[i], srcstr[i + 1], srcstr[i + 2], 0, 3);
cnt += 2;
dstlength[0] = cnt;
return 0;
}
else if (i == num - 2)
{
base64_convert4(dststr + cnt, srcstr[i], srcstr[i + 1], 0, 0, 2);
cnt += 1;
dstlength[0] = cnt;
return 0;
}
else
{
return 1;
}
return 1;
}
測試
int main(void)
{
char man[] = "Man";
char trans_man[3][20];
int len[3];
//test encode
for (int i = 0; i < 3; i++)
{
base64_encoder(trans_man[i], man, 3 - i, len + i);
trans_man[i][len[i]] = '\0';
printf("%s\n", trans_man[i]);
}
//test decode
char restore[3][20];
int lenres[3];
for (int i = 0; i < 3; i++)
{
base64_decoder(restore[i], trans_man[i], 4, lenres + i);
restore[i][lenres[i]] = '\0';
printf("%s\n", restore[i]);
}
//test for situation without '='
base64_decoder(restore[0], trans_man[1], 3, lenres);
restore[0][lenres[0]] = '\0';
printf("%s\n", restore[0]);
base64_decoder(restore[0], trans_man[2], 2, lenres);
restore[0][lenres[0]] = '\0';
printf("%s\n", restore[0]);
char myself[] = "include-yy";
char myself_encode[20];
int myself_len;
base64_encoder(myself_encode, myself, 10, &myself_len);
myself_encode[myself_len] = '\0';
printf("\n%s\n", myself_encode);
base64_decoder(restore[0], myself_encode, myself_len, lenres);
restore[0][lenres[0]] = '\0';
printf("%s\n", restore[0]);
return 0;
}
輸出結(jié)果應(yīng)該為:
TWFu
TWE=
TQ==
Man
Ma
M
Ma
M
aW5jbHVkZS15eQ==
include-yy
在 Python 中使用 Base64
Python 隨處可見,用起來也比 C 方便的多,故學(xué)習(xí) Python 的用法肯定還是有用的。
Python 的 base64 模塊
base64 模塊提供了將二進(jìn)制數(shù)據(jù)編碼為可打印 ASCII 字符和將編碼解碼為二進(jìn)制數(shù)據(jù)的函數(shù)。
base64 模塊提供的函數(shù)不限于操作 base64,還有 base16,base32 等,不過這里只關(guān)注 base64 的編碼和解碼。
接口函數(shù)
base64.b64encode(s, altchars=None)
該函數(shù)將類字節(jié)對象(bytes-like object)使用 base64 進(jìn)行編碼,并返回編碼后的字節(jié)。
altchars 必須是長度至少為 2 的類字節(jié)對象,這些字符指定了代替 + 和 / 的字符。這就允許該函數(shù)生成 URL 安全的 Base64 字符串。
base64.b64decode(s, altchars=None, validate=False)
該函數(shù)被編碼的字符串解碼并返回解碼字節(jié)。
altchars 必須是長度至少為 2 的類字節(jié)對象或 ASCII 字符串,這些字符串指定了用來代替 + 和 / 的字符。
validate 默認(rèn)為 False ,既不是 base-64 字符也不是備用字母表中的字符會被丟棄。如果 validate 為 True ,非 base64 字符會導(dǎo)致 binascii.Error 。
具體用法
首先,導(dǎo)入 python 的 base64 模塊
import base64
創(chuàng)建一個字符串,并使用 encode 方法,將其轉(zhuǎn)化為字節(jié)對象:
s = 'include-yy'
s = s.encode()
(encode 方法的默認(rèn)編碼是 'uft-8')
接著使用 base64.b64encode() ,便可得到結(jié)果:
base.b64encode(s)
想要解碼,則調(diào)用 base64.b64decode()
s2 = base.b64encode(s)
base64.b64decode(s2.encode());
參考資料
總結(jié)
以上是生活随笔為你收集整理的对象base64转码_什么是 Base64 编码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【笔记】蒙特卡洛树搜素(MCTS)与三子
- 下一篇: 没有目标的人生是可怕的!