天天看點

了解base64的原理, 并用PHP實作

開發者對Base64編碼肯定很熟悉,是否對它有很清晰的認識就不一定了。實際 上Base64已經簡單到不能再簡單了,如果對它的了解還是模棱兩可實在不應該。大概介紹一下Base64的相關内容,花幾分鐘時間就可以徹底了解它。文 章下邊貼了一個Base64的編解碼器,友善閱讀文章的同時來實驗。

  一. Base64編碼由來

  為什麼會有Base64編碼呢?因為有些網絡傳送管道并不支援所有的位元組,例如傳統的郵件隻支援可見字元的傳送,像ASCII碼的控制字元就 不能通過郵件傳送。這樣用途就受到了很大的限制,比如圖檔二進制流的每個位元組不可能全部是可見字元,是以就傳送不了。最好的方法就是在不改變傳統協定的情 況下,做一種擴充方案來支援二進制檔案的傳送。把不可列印的字元也能用可列印字元來表示,問題就解決了。Base64編碼應運而生,Base64就是一種 基于64個可列印字元來表示二進制資料的表示方法。

  二. Base64編碼原理

  看一下Base64的索引表,字元選用了"A-Z、a-z、0-9、+、/" 64個可列印字元。數值代表字元的索引,這個是标準Base64協定規定的,不能更改。64個字元用6個bit位就可以全部表示,一個位元組有8個bit 位,剩下兩個bit就浪費掉了,這樣就不得不犧牲一部分空間了。這裡需要弄明白的就是一個Base64字元是8個bit,但是有效部分隻有右邊的6個 bit,左邊兩個永遠是0。

了解base64的原理, 并用PHP實作

  那麼怎麼用6個有效bit來表示傳統字元的8個bit呢?8和6的最小公倍數 是24,也就是說3個傳統位元組可以由4個Base64字元來表示,保證有效位數是一樣的,這樣就多了1/3的位元組數來彌補Base64隻有6個有效bit 的不足。你也可以說用兩個Base64字元也能表示一個傳統字元,但是采用最小公倍數的方案其實是最減少浪費的。結合下邊的圖比較容易了解。Man是三個 字元,一共24個有效bit,隻好用4個Base64字元來湊齊24個有效位。紅框表示的是對應的Base64,6個有效位轉化成相應的索引值再對應 Base64字元表,查出"Man"對應的Base64字元是"TWFU"。說到這裡有個原則不知道你發現了沒有,要轉換成Base64的最小機關就是三個位元組,對一個字元串來說每次都是三個位元組三個位元組的轉換,對應的是Base64的四個位元組。這個搞清楚了其實就差不多了。

了解base64的原理, 并用PHP實作
了解base64的原理, 并用PHP實作

但是轉換到最後你發現不夠三個位元組了怎麼辦呢?願望終于實作了,我們可以用兩 個Base64來表示一個字元或用三個Base64表示兩個字元,像下圖的A對應的第二個Base64的二進制位隻有兩個,把後邊的四個補0就是了。是以 A對應的Base64字元就是QQ。上邊已經說過了,原則是Base64字元的最小機關是四個字元一組,那這才兩個字 符,後邊補兩個"="吧。其實不用"="也不耽誤解碼,之是以用"=",可能是考慮到多段編碼後的Base64字元串拼起來也不會引起混淆。由此可見 Base64字元串隻可能最後出現一個或兩個"=",中間是不可能出現"="的。下圖中字元"BC"的編碼過程也是一樣的。

了解base64的原理, 并用PHP實作

  三. 總結  

  說起Base64編碼可能有些奇怪,因為大多數的編碼都是由字元轉化成二進制的過程,而從二進制轉成字元的過程稱為解碼。而Base64的概念就恰好反了,由二進制轉到字元稱為編碼,由字元到二進制稱為解碼。

function base64encode($str) {
    $base64 = [
        "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', '+', '/'
    ];

    $len = strlen($str);
    $rstr = "";

    for($j=0; $j<$len/3; $j++) {
        $item = substr($str, $j*3, 3);
        $itemlen = strlen($item);
        $eightbit = "";

        for($i=0; $i<=$itemlen; $i++) {
            $bin[$i] = decbin(ord($item[$i]));
            $combin[$i] = str_pad($bin[$i], 8, "0", STR_PAD_LEFT);
            $eightbit .= $combin[$i];
        }

        for ($i = 0; $i <= $itemlen; $i++) {
            $sixbit = substr($eightbit, $i * 6, 6);
            $rstr .= $base64[bindec($sixbit)];
        }

        $pad = ["==", "=", ""];
        $rstr .= $pad[$itemlen-1];
    }
    return $rstr;
}

echo base64_encode("Maxwelldu");

$r = base64encode("Maxwelldu");
echo $r;