詳細瞭解base64編碼和解碼

base64編碼

公司技術一起開會,做任何項目都經常會討論編碼的問題。在這種時候,你就算裝不了逼,也不能一臉懵逼呀。編碼技術太多,但是常用的基礎的必須瞭解,今天就詳細講解一下base64編碼,包括base64的坑:url中使用base64編碼的注意事項,不同編碼的base64解碼之後的內容卻是一樣的等問題,這些都瞭解了,base64就能真正成爲你自己的技術了。

備註:首先base64就是編碼,這不是壓縮,編碼是會增加字節數的,同時base64算法可逆,不能用於加密,雖然他也叫加密算法,但是我覺得叫base64編碼更貼切。

優勢:

  • 算法簡單,幾乎不影響效率。
  • 算法可逆,解碼方便。
  • 加密後的字符串只有[0-9a-zA-Z+/=], 不可打印字符(包括轉移字符)也可傳輸。
  • 跨語言(java,php,python...),跨平臺(windows,mac,linux,unix....),跨編碼(utf8,gbk...),這也是其最重要的優勢

爲什麼說算法簡單,base64總共只有[0-9a-zA-Z+/=],0-63共64個編碼字符,就是說所有被編碼的字符都會通過下邊這張遍進行編碼,選擇二進制進行轉換,我覺着主要是因爲計算機底層就是二進制,用其他進制還得轉換成二進制。爲什麼有的base64編碼有=號呢,這麼說有些人還是有些懵逼,沒事下邊就解釋怎麼編碼:

下邊順便把不可打印字符、轉移字符和打印字符截張圖:

base64編碼:

base64編碼一個字節用6位二進制標示,一個字節是8位二進制,三個字節正好是3*8=24個位,可以生成4個base64字符。比如字符串爲 "123",1 的 ASCII 爲 49,轉換爲二進制就是 00110001,2 的 ASCII 爲 50,轉換爲二進制就是 00110010,3 的 ASCII 爲 51,轉換爲二進制就是 00110011,這三個二進制組合在一起就是這樣:001100010011001000110011
上面的二進制位總共 24 位,每6位生成一個base64字符:

  • 第一個001100,通過編碼表查出是第12個:M
  • 第二個010011,通過編碼表查處是第19個:T
  • 第三個001000,通過編碼表查處是第8個:I
  • 第四個110011,通過編碼表查處是第51個:z

那麼123編碼之後就是:MTIz

此編碼正好是6的倍數,如果不是這麼巧呢,多出一個字符變成32位二進制了呢,或者少一個字符,變成16位二進制了呢,都無法整除6,那就需要補齊。

補齊:

如果該字符多一個字節,變成了"1234",成了4*8=32位,base64編碼,6個一組,最多整好分成5組,還多2位,我們就給他補上4個0000,那字符"1234"原來的二進制爲:001100010011001000110011,現在多了4個0:001100010011001000110011001101000000,現在變成36位了,然後在進行分割,查表得MTIzNA,但是爲了後面的解碼,我們需要在加密後的字符串末尾加上 2 個 “=”,就是 “MTIzNA==”。

如果剩下 2 個字節的話變成,字符變成了"12",2 個字節剛好 16 位,6 位一組的話,需要3*6=18位,也就是說,少了 2 位,這樣就可以組合成 18 位了(3 個 Base64 字符),這裏我們以字符串 “12” 爲例,1 的 ASCII 轉換爲二進制是 00110001,2 的 ASCII 轉換爲二進制是 00110010,我們將它組合在一起然後補齊之後(加上 2 個 0),就是 001100010011001000,按照 6 位一組進行分割,然後查表求得,結果是 MTI,但是爲了後面的解碼,我們需要在加密後的字符串末尾加上 1 個 “=”,就是 “MTI=”。

從這裏也就知道一個=表示兩個00,解碼的時候根據=相應的減掉多餘的0就可以了。

跨編碼:如漢字編碼,不管是utf8下一個漢字3個字節還是gb2312下一個字符2個,都是按照上邊的規則來進行編碼的,所以可以跨編碼。它不受其他編碼的影響,仍然保持不變,這點很有意義,如下驗證:   

String a = "123412312sfwefwefwefw";
        
        String b = new String(CodecManager.getCodecClient(CodecConstants.BASE64).encode(a.getBytes()));
        System.out.println(b);
        
        //對base64編碼後的字符串,進行其他編碼
        String c1 = new String(b.getBytes(),"gbk");
        System.out.println(c1);
        
        String c2 = new String(b.getBytes(),"utf-8");
        System.out.println(c2);
        
        String c3 = new String(b.getBytes(),"ISO8859-1");
        System.out.println(c3);

輸出:

MTIzNDEyMzEyc2Z3ZWZ3ZWZ3ZWZ3
MTIzNDEyMzEyc2Z3ZWZ3ZWZ3ZWZ3
MTIzNDEyMzEyc2Z3ZWZ3ZWZ3ZWZ3
MTIzNDEyMzEyc2Z3ZWZ3ZWZ3ZWZ3

跨語言:

<?php

echo base64_encode('123412312sfwefwefwefw');

輸出:MTIzNDEyMzEyc2Z3ZWZ3ZWZ3ZWZ3

下邊再來個漢字的例子:將字符"我是A"這字符的ASCII 轉換爲二進制:utf8下,一個漢字爲3個字節就是24位,兩個就是48位,加上一個字節A是8位,總共是56位:11100110100010001001000111100110100110001010111101000001。6*10=60,補4個0000,可以自己在線搜索漢字轉ASCII碼:

最終6位分割對照表得到:5oiR5pivQQ==

base64解碼:

解碼就簡單多了,比如剛纔5oiR5pivQQ==這個,將這個字符根據對照表解碼成二進制,然後去掉補齊4個0000,然後組合起來再去ASCII裏邊反編譯(這裏邊就是一個字符8位了)出來就行了。

遇到的問題:

  1. 有的base64編碼完了,通過url傳遞,後臺接收到後,反編譯會失敗。是因爲url中的+和/符號會被瀏覽器在傳輸的過程中處理成別的符號,造成解碼失敗。這個不是base64的問題,base64又不能預料到使用他的環境。解決方案:編碼後,將+/轉換成-*等其他不會被瀏覽器處理的符號,後臺接受後,先將處理過的特殊符號轉回來再解碼。
  2. 不同編碼的字符,反編碼之後內容相同:如已編碼字符:'ZEVWSGRGWldlbEIwVm5WRn========'和'ZEVWSGRGWldlbEIwVm5WRn'通過上邊的學習,我們知道你後邊加多少=,都是無用的,補齊的數據,反編譯的時候會去掉。還有如:'ZEVWSGRGWldlbEIwVm5WRn'和'ZEVWSGRGWldlbEIwVm5WRg'的解碼結果相同都是:dEVHdFZWelB0VnVF ,這是因爲Rg, Rh, Ri, ...通通表示字母F。雖然編碼後的結果不同,但不影響解碼後的結果。

以上就是我總結的base64編碼問題。雖說不是很難,也是整理了一上午。

借鑑的博客:https://learnku.com/articles/36655

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章