第一部分:主流編碼類型
第二部分:編碼兼容問題
ASCII只有127個字符,表示英文字母的大小寫、數字和一些符號,一個字符用8位(bit)即1字節(byte)表示,如:A(65),Z(90),a(97),z(122)。但是要處理中文顯然一個字節是不夠的,至少需要兩個字節,而且還不能和ASCII編碼沖突,所以中國制定了GB2312編碼,用來把中文編進去,同樣其他國家的語言也有屬於自己的編碼格式。
由於每個國家的語言都有屬於自己的編碼格式,在多語言編輯文本中會出現亂碼,這樣Unicode即萬國碼應運而生,Unicode就是將這些語言統一到一套編碼格式中,通常兩個字節表示一個字符,而ASCII是一個字節表示一個字符,這樣如果你編譯的文本是全英文的,用Unicode編碼比ASCII編碼需要多一倍的存儲空間,在存儲和傳輸上就十分不划算。
為了節省空間又出現了把Unicode編碼轉化為“可變長編碼”的UTF-8編碼,UTF-8編碼將Unicode字符按數字大小編碼為1-6個字節,英文字母被編碼成1個字節,常用漢字被編碼成3個字節,如果你編譯的文本是純英文的,那么用UTF-8就會非常節省空間,並且ASCII碼也是UTF-8的一部分。
國標碼,只包含英文字符和自己國家的字符,用8位(bit)1字節(byte)表示英文字符,用16位(bit)2字節(byte)表示中文字符。
計算機系統通用的字符編碼工作方式
(1)在計算機內存中,統一使用Unicode編碼,當需要保存到硬盤或者需要傳輸的時候,就轉換為UTF-8編碼。
(2)用記事本編輯的時候,從文件讀取的UTF-8字符被轉換為Unicode字符到內存里,編輯完成后,保存的時候再把Unicode轉換為UTF-8保存到文件。
字母A用ASCII編碼是十進制的65,二進制的01000001;
字符0用ASCII編碼是十進制的48,二進制的00110000;
漢字'中'已經超出了ASCII編碼的范圍,用Unicode編碼是十進制的20013,二進制的01001110 00101101;
如果把ASCII編碼的A用Unicode編碼,只需要在前面補0就可以,因此,A的Unicode編碼是00000000 00110000;
如果把ASCII編碼的0用Unicode編碼,只需要在前面補0就可以,因此,A的Unicode編碼是00000000 01000001;
▷1位 = 1bit
▷8bits = 1bytes = 1字節
▷1024bytes = 1KB
▷1024KB = 1MB
▷1024MB = 1GB
▷1024GB = 1TB
PYTHON2.x默認使用ASCII編碼,PYTHON3.x默認使用UTF-8編碼
# 查看當前版本的編碼格式
import sys
encode_type = sys.getdefaultencoding()
# python2_test.py
message = "Hello, China!"
print(message)
print(len(message))
message = "你好,中國!"
print(message)
print(len(message))
# 報錯如下:
"""
File "python2_test.py", line 5
SyntaxError: Non-ASCII character '\xe4' in file py2_test.py on line 5,
but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details
"""
# 報錯原因:
"""
PYTHON2默認ASCII編碼格式,也就是默認使用ASCII編碼格式將代碼讀入內存,由於ASCII碼中沒有中文字符導致報錯。
"""
# python3_test.py
message = "Hello, China!"
print(message)
print(len(message))
message = "你好,中國!"
print(message)
print(len(message))
# 結果:
"""
Hello, China!
13
你好,中國!
18
"""
# 說明:
"""
PYTHON3默認UTF-8編碼格式,也就是默認使用UTF-8編碼格式將代碼讀入內存,由於UTF-8是可變的Unicode編碼,所以可以讀取中文和英文。
"""
# -*- coding: utf-8 -*-
message = "Hello, China!"
print(message)
print(len(message))
message = "你好,中國!"
print(message)
print(len(message))
# 結果:
"""
Hello, China!
13
你好,中國!
18
"""
2.3.PYTHON編碼的兼容方法
①.編碼問題分析
PYTHON2.X開發環境:輸出 "Hello, World!",英文沒有問題,但是如果你輸出中文字符 "你好,世界" 就有可能會碰到中文編碼問題。
報錯產生:如果在我們的PY腳本程序中存在中文,運行文件時會報錯;如果不含有中文就不會出現問題。
原因分析:PYTHON2.X中默認的編碼格式是ASCII 格式,在沒修改編碼格式時無法正確打印輸出漢字,所以在讀取中文時會報錯。
處理方法:只要在文件開頭加入 # -*- coding: UTF-8 -*-或者# coding=utf-8,注意:# coding=utf-8的'='號兩邊不要空格。
注意事項:Python3.X 源碼文件默認使用utf-8編碼,所以可以正常解析中文,無需指定 UTF-8 編碼。另外如果你使用編輯器,同時需要設置python文件存儲的格式為UTF-8,否則會出現類似錯誤信息。
Pycharm設置步驟:進入 file > Settings,在輸入框搜索 encoding。找到 Editor > File encodings,將 IDE Encoding 和 Project Encoding 設置為utf-8。
②.編碼問題處理
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
第一行注釋是為了告訴Linux/OS X系統,這是一個Python可執行程序,Windows系統會忽略這個注釋;
第二行注釋是為了告訴Python解釋器,按照UTF-8編碼讀取源代碼,否則,你在源代碼中寫的中文輸出可能會有亂碼。
申明了UTF-8編碼並不意味着你的.py文件就是UTF-8編碼的,必須並且要確保文本編輯器正在使用UTF-8 without BOM編碼:
2.4.PYTHON字符串編碼問題
①.python2.x和python3.x中文輸出的對比
# python2的中文輸出
python2_string = u'中國'
print(python2_string)
# python3的中文輸出
python3_string = '中國'
print(python3_string)
代碼分析:在Python2.x中,字符串是以ASCII編碼形式讀到內存中,由於ASCII碼長度不足以讀取到中文字符,則需要轉碼為Unicode編碼后讀到內存中;在Python3.x中,字符串是以Unicode編碼讀到內存中的,即字符串支持多語言。
②.單個字符的ASCII/Unicode編碼轉換
# 單個字符的編碼轉換
<1>.ord()函數獲取字符的整數形式表示
ord('A')
ord('a')
ord('中')
ord('國')
<2>.chr()函數把編碼轉換為對應的字符
chr(65)
chr(97)
chr(20013)
chr(22269)
<3>.已知字符整數編碼十六進制表示中文
s = '\u4e2d\u56fd' -> 中國
③.str類型和bytes類型的對比
基礎理論:在Python中字符串類型是str,在內存中以Unicode表示,一個字符對應若干個字節。如果要在網絡上傳輸,或者保存到磁盤上,就需要把str變為以字節為單位的bytes。bytes類型的數據用帶b前綴的單引號或雙引號表示。
s1 = 'hello'
print(s1)
s2 = b'hello'
print(s2)
代碼分析:前者s1是str類型,后者s2是bytes類型,后者雖然內容顯示得和前者一樣,但bytes的每個字符都只占用一個字節。
④.str類型和bytes類型的轉換
基礎理論:Unicode表示的str字符串通過encode()方法可以編碼為指定的bytes。
s1 = 'hello'.encode('utf8')
# > b'hello'
s2 = 'hello'.encode('ascii')
# > b'hello'
c_bytes1 = '中國'.encode('utf8')
# > b'\xe4\xb8\xad\xe5\x9b\xbd'
c_tytes2 = '中國'.encode('ascii')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
代碼說明:英文的str可以用ASCII編碼為bytes,內容是一樣的,含有中文的str可以用UTF-8編碼為bytes。含有中文的str無法用ASCII編碼,因為中文編碼的范圍超過了ASCII編碼的范圍,解釋器會報錯。另外在bytes中,無法顯示為ASCII字符的字節,用\x##顯示。
基礎理論:如果我們從網絡或磁盤上讀取了字節流,那么讀到的數據就是bytes。就需要用decode()方法把bytes變為str。
s1 = b'hello'.decode('ascii')
# > 'hello'
s2 = b'hello'.decode('utf-8')
# > 'hello'
s3 = b'\xe4\xb8\xad\xe5\x9b\xbd'.decode('utf-8')
# > '中國'
s4 = b'\xe4\xb8\xad\xe5\x9b\xbd\xff'.decode('utf-8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 6: invalid start byte
# 如果bytes中只有一小部分無效的字節,可以傳入errors='ignore'忽略錯誤的字節
s = b'\xe4\xb8\xad\xe5\x9b\xbd\xff'.decode('utf-8', errors='ignore')
# > '中國'
⑤.str類型和bytes類型的長度
基礎理論:str包含多少個字符,可以用len()函數,len()函數計算的是str的字符數,如果換成bytes,len()函數就計算字節數.
s_len = len('hello')
# > 5
b_len = len(b'hello')
# > 5
c_len = len(b'\xe4\xb8\xad\xe5\x9b\xbd')
# > 6
c_len= len('中國'.encode('utf-8'))
# > 6
代碼分析:1個中文字符經過UTF-8編碼后通常會占用3個字節,而1個英文字符只占用1個字節。在操作字符串時,我們經常遇到str和bytes的互相轉換。為了避免亂碼問題,應當始終堅持使用UTF-8編碼對str和bytes進行轉換。