字符與編碼的問題,之前很少深究,但這次遇到了base64的問題,所以覺得是時候解決一下了,不一定全面,但想盡可能記錄一些想知道的點。。。
- 首先,為什么需要編碼??因為計算機本身可不認識:‘你在做什么?’、‘what are you doing?’等這么人類性的語言;在計算機內部,所有的信息都表示為一個二進制的字符串。而每一個二進制位(bit)有0和1兩種狀態,具體哪些二進制數表示什么字符,多少位表示什么字符,需要有一個標准,也就是編碼。
- 字節(Byte):是計量存儲容量的單位,是構成信息的一個很小的單位,上面還有KB、MB、GB、TB、PB、EB、ZB、YB等;下面還有bit(位)。
- 字符:各種文字、符號的總稱。例如文字、標點、圖形、數字、字母等等。
- 字符集:顧名思義,就是一定數量的字符組成的集合,字符集種類比較多,而且每個字符集包含的字符個數也不同,常見字符集主要有:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等,字符集為每一個【字符】分配一個唯一的 ID(學名為碼位 / 碼點 / Code Point)。
- 字符編碼:將字符集中的每個字符映射為字節流的實現方案(編碼方案),即屬於將【碼位】轉換為字節序列的規則,便於計算機存儲和傳輸;常見的字符編碼有ASCII編碼、UTF-8編碼、GBK編碼、Base64編碼等。某種意義上來說,字符集與字符編碼有種對應關系,例如 ASCII字符集對應有ASCII編碼。
- 編碼與解碼:編碼的過程是將字符轉換成字節流,解碼的過程是將字節流解析為字符。
1. ASCII碼:(American Standard Code for Information Interchange) 美國信息交換標准碼,是美國制定的單字節字符編碼系統,作用於ASCII字符集,見附錄1。
因為在英語中,128個符號就可以滿足,所以一直將1個字節(8位)的最高位閑置(默認為0),其他7位用於編碼;后來才擴展了最高位,共可以表示256個符號。
例如:字符A,ASCII碼是65(十進制),二進制是01000001,八進制是0101,十六進制是0x41。
但是,即使256位,也是不夠用的,因為世界上的國家和文字、符號太多了,於是就開始擴展,出現了Unicode字符集。
2. Unicode字符集
注意:Unicode是一個符號集,包含了各國的符號、文字等,詳細可查;它只規定了字符對應的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲。
於是出現了Unicode字符集對應的編碼方式,主要是utf-8,也有utf-16、utf-32等,但utf-8是在互聯網上使用最廣的一種Unicode的實現方式。utf-8最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個字節表示一個符號,並且根據不同的符號需要而變化字節長度。
3. utf-8編碼
utf-8的編碼規則是:
1)對於單字節的符號,字節的第一位設為0,后面7位為這個符號的unicode碼。所以對於英文字符,utf-8編碼和ASCII碼相同。
2)對於n字節的符號(n>1),第一個字節的前n位都為1,第n+1位為0,(其第一個字節從最高位開始,連續的二進制位為1的個數決定了其編碼的字節數n),后面各字節的前兩位一律為10。剩下的沒有提及的二進制位,全部為這個符號的unicode碼。
其編碼規則如下:
# ---------------------------------------------------------------
Unicode符號范圍(十六進制) | UTF-8編碼方式(二進制)
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
# ---------------------------------------------------------------
4. 示例,獲取漢字 '龍' 的utf-8編碼值
第一步,獲取 '龍' 的Unicode值:ord('龍') —— python內置函數ord,得到:40857
第二步,求得Unicode值的十六進制:hex(40857) —— 得到:9f00
第三步,獲取utf-8編碼:其實python有函數進行編碼 —— '龍'.encode('utf-8'),可得:b'\xe9\xbe\x99',也即是:e9be99
其求取方法如下:
(1)求取 Unicode值對應的二進制值:十進制轉二進制 —— python內置函數,bin(40857),得到:'0b1001111110011001',即:'1001111110011001'。
(2)由其十六進制:9f00,對應utf-8的編碼規則,0800<9f00<ffff,所以屬於三字節類型,上述規則第三行,即屬於:'1110xxxx 10xxxxxx 10xxxxxx' 類型。
(3)從得到的二進制中,最后一個二進制位開始,依次從后向前代入上述格式中的 'x' 中,多出的位補0。
(4)於是得到:11101001 10111110 10011001,對應的十六進制為:e9 be 99,因為每4位二進制組成一個十六進制。
# ----------------------------------------------------------------
5. 獲取 utf-8 編碼的字節長度 —— 先編碼,再len()
b = '龍'.encode('utf-8') # utf-8 編碼 print('b:',b) # 結果中開頭的 b 字符表示bytes類型,'\x'表示十六進制數據 print(type(b)) # 類型是'bytes' print(len(b)) # 編碼的字節數,3個字節編碼,因為b就是字節類型 bytes
6. utf-8 解碼和編碼
t = '大' str_ = t.encode('utf-8') # 編碼得到字節流 print(str_) uni_ = str_.decode('utf-8') # 通過字節流,進行解碼獲取字符串 print(uni_)
7. 英文字母在計算機內存儲的是機內碼,也就是ASCII碼,因為英文字母的機內碼就是ASCII碼。而每一個漢字在不同的編碼下也有對應的內碼。
utf-8 用3個字節來表示常用的一個中文字符,1個字節來表示英文字符,兼容ASCII碼;
gbk、gb2312 用2個字節來表示一個中文字符,1個字節來表示英文字符,兼容ASCII碼,gbk向下兼容gb2312,gb18030向下兼容gbk和gb2312;
b = 'm'.encode('gbk') print('b:',b) # 開頭的 b 字符表示bytes類型 print(type(b)) #類型是'bytes' print(len(b)) #編碼的字節數,因為上面先進行了編碼
8. sys.getsizeof() 返回對象的內存大小:不僅僅包括字符等數據的字節數,還包含很多附帶的內存開銷,例如垃圾收集器等;就像一個word文檔,即使什么都沒寫,也會有9kB的大小,因為它包含了word的頭部信息。
附錄1:ASCII碼表
參考:
https://www.cnblogs.com/wangxiaorui/p/5287975.html
https://baike.baidu.com/item/ASCII/309296?fromtitle=ascii%E7%A0%81&fromid=99077&fr=aladdin
https://tool.oschina.net/commons?type=4
https://zhuanlan.zhihu.com/p/25148581