Base64 是一種基于64個可打印字符來表示二進制數(shù)據(jù)的表示方法得院,編碼后的數(shù)據(jù)比原始數(shù)據(jù)略長像樊,為原來的 4/3拾碌。編碼表由64 個字符組成:26個大寫英文字母 + 26個小寫英語字母 + 數(shù)字0-9 + 符號 "+" + 符號 "/"躏哩,其 ASCII 值為 0-63
編碼過程:
1奏候、每 3 個字節(jié)組成一組,(3*8)組成一串24個進制位未巫;例:111001101001010110001111
2、將 24 個進制位分 4 組启昧,每組 6 個進制位叙凡,在前補 00 擴展成 32 個進制位 = 4 個字節(jié);例:00111001 00101001 00010110 00001111
3密末、將每個字節(jié)轉(zhuǎn)十進制握爷,匹配編碼表對應(yīng)字符,組成新的 4 個字符
Base64 編碼表
image.png
滿足 3 個字節(jié)情況下(utf-8 字符集一個中文字符等于 3 個字節(jié))
原文本 Min 經(jīng)過 base64 編碼后等到新字符 TWlu
image.png
不滿足 3 個字節(jié)严里,2 個字節(jié)情況下新啼,在編碼后的字符末尾填充1個 = 號
原文本 Mi 經(jīng)過 base64 編碼后等到新字符 TWk=
image.png
不滿足 3 個字節(jié),1 個字節(jié)情況下刹碾,在編碼后的字符末尾填充 2 個 = 號
原文本 M 經(jīng)過 base64 編碼后等到新字符 TQ==
image.png
utf-8 中文字符『敏』燥撞,其對應(yīng)的16進制是 E6958F,轉(zhuǎn)換二進制為:111001101001010110001111 占三個字節(jié)
image.png
代碼
<?php
namespace Patterns\Algorithm;
class Base64
{
private $_encode_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
private $_strpad = ['==', '=', ''];
/**
* 輸入字符以3個字節(jié)分組,3 個字節(jié) = base64 4個字節(jié)
* 3個字節(jié)為一組
* * 第一個字符 = 輸入第一個字符右移 2 位
* * 第二個字符 = 輸入第一個字符左移 4 位 + 輸入第二個字符右移 4 位
* * 第三個字符 = 輸入第一個字符左移 2 位 + 輸入第三個字符右移 6 位
* * 第四個字符 = 取輸入第三個字符右 6 位
* base64 編碼
* @param string $str
* @return string
*/
public function encodebit(string $str):string
{
$strLen = strlen($str);
$subIndex = 0;
$subStr = $out = '';
while ($strLen > 2){
$subStr = substr($str, $subIndex, 3);
$fir = ord($subStr[0]) >> 2;
$sc = ((ord($subStr[0]) & 3) << 4) + ((ord($subStr[1])) >> 4);
$th = ((ord($subStr[1]) & 15) << 2) + (ord($subStr[2]) >> 6);
$end = ord($subStr[2]) & 63;
$out .= $this->_encode_table[$fir];
$out .= $this->_encode_table[$sc];
$out .= $this->_encode_table[$th];
$out .= $this->_encode_table[$end];
$subIndex += 3;
$strLen -= 3;
}
if(0 === $strLen){
return $out;
}
$str = substr($str, $subIndex);
$out .= $this->_encode_table[ord($str[0]) >> 2];
if($strLen > 1){
$out .= $this->_encode_table[((ord($str[0]) & 3) << 4) + (ord($str[1]) >> 4)];
$out .= $this->_encode_table[(ord($str[1]) & 15) << 2];
$out .= '=';
} else {
$out .= $this->_encode_table[(ord($str[0]) & 3) << 4];
$out .= '==';
}
return $out;
}
/**
* 編碼
* base64 將 3 個字節(jié)轉(zhuǎn)成 4 個字節(jié)
* 每 3 個字節(jié)組成一組物舒,(3*8)組成一串24個進制位
* 將 24 個進制位分 4 組色洞,每組 6 個進制位,在前補 00 擴展成 32 個進制位 = 4 個字節(jié)
* 將每個字節(jié)轉(zhuǎn)十進制冠胯,匹配編碼表對應(yīng)字符火诸,組成新的 4 個字符
*
* 不足 3 個字節(jié)情況:
* 2 個字節(jié):按照上述步驟可組成新的 3 個字符,在末尾補充 1 個 = 號荠察,即 xxx=
* 1 個字節(jié):按照上述步驟可組成新的 2 個字符置蜀,在末尾補充 2 個 = 號,即 xx==
* @param string $data
* @return string
*/
public function encode(string $data):string
{
$strLen = strlen($data);
$subIndex = 0;
$out = '';
$offset = ceil($strLen / 3);
for($i = 0; $i < $offset; $i++){
$binstr = '';
$subStr = substr($data, $subIndex, 3);
$strLen = strlen($subStr);
for($k = 0; $k < $strLen; $k++){
$decbin = decbin(ord($subStr[$k]));
$binstr .= str_pad($decbin, 8, 0, STR_PAD_LEFT);
}
$out .= $this->bintoChar($strLen, $binstr);
$subIndex += 3;
}
$out .= $this->_strpad[$strLen - 1];
return $out;
}
/**
* 將8位二進制組成的進制位進行分組悉盆,6 位一組:每組在前方增 00 填充成 8 位盯荤,不足8位則在后方補 0
* @param int $strSize
* @param string $bin
* @return string
*/
private function bintoChar(int $strSize, string $bin):string
{
$subIndex = 0;
$char = '';
while ($strSize >= 0){
$substr = '00' . substr($bin, $subIndex, 6);
$substr = str_pad($substr, 8, 0, STR_PAD_RIGHT);
$char .= $this->_encode_table[bindec($substr)];
$subIndex += 6;
$strSize--;
}
return $char;
}
}
$base64 = new Base64();
$str = 'test';
echo 'base: ' . base64_encode($str),PHP_EOL;
echo 'base: ' . $base64->encodebit($str). PHP_EOL;
echo 'base: ' . $base64->encode($str) . PHP_EOL;