這裏的字符指的是 人類能識別的字符,字符方便記憶。在計算機中,都是按照二進制存儲,方便存儲的是數字,因此,字符在計算機中的存儲須要先轉換爲數字,而後再進行存儲。
存儲過程:字符 --> 數字,讀取過程:數字 --> 字符 ~
以上兩個轉換過程當中,字符和數字之間存在一個一一對應的關係,一個字符對應一個特定的數字,這個一一對應的關係就是所謂的字符編碼~python
計算機出現以後,美國人搞出了一套ASCII,以下圖。ASCII表中一個字符使用一個字節來表示,一個字節8位,最多隻能表示256個字符(2**8 = 256)。表中的字符包括英文、字母、數字,還有一些特殊字符,這些字符和數字 之間存在一一對應關係,例如:97表示小寫的a,65表示大寫的A...windows
其實ASCII僅使用了一個字節中的7位來表示字符,一共127個字符(後128個稱爲擴展ASCII碼)。ASCII是美國人發明的,因此除了英文,不能用於其餘語言。因而中國人就指定了gb2312編碼,其中包含了中文在內的字符 --> 數字的對應關係,日本人也制定了 Shift_JIS 編碼,韓國人的 Euc-kr 編碼等等,各國的人都有本身的一套標準。網絡
這樣在使用過程當中,例如,一篇文檔中僅有一種語言,那麼 就不存在問題;可是若是這篇文檔中存在多種語言,那麼不論是用哪一種編碼標準,都會存在亂碼問題,這時候 unicode 就應運而生,unicode 可以兼容萬國字符,從而避免以上狀況出現的亂碼問題~ide
unicode 經常使用2個字節(16位二進制)表明一個字符,生僻字須要用4個字節。unicode 兼容 ascii,例如:小寫字母x,用 ascii 表示是 0111 1000(二進制),使用 unicode 表示 爲 0000 0000 0111 1000(二進制),可見 二者的值一致,只是使用 unicode 表示使用了2個字節,而是用 ascii 表示僅使用了一個字節,存儲空間多了一倍~編碼
unicode 中存放了與其餘編碼的映射關係,因此 unicode 能夠兼容 萬國字符。"unicode 中存放了與其餘編碼的映射關係" 這句話是否是很差理解,簡單的說就是,unicode 編碼 能夠轉爲 其餘編碼,例如gbk、Shift_JIS等,其餘的編碼也能夠經過 unicode 中存在的映射關係 轉爲 unicode 編碼,轉換的規則以下圖:code
經過以下示例來更進一步的解釋,這張圖表示 unicode 編碼 和 gbk 等其餘編碼的映射關係(unicode映射表,截取而來),中文 '人' 字的 unicode 編碼是 '4eba',對應的 gbk 編碼是 '484b'orm
python3環境blog
>>> x = '\u4eba' # unicode 碼 >>> x '人' >>> x.encode('gbk') # 轉爲 gbk 編碼 b'\xc8\xcb' >>> b'\xc8\xcb'.decode('gbk') # gbk 編碼轉爲 unicode 碼 '人'
單引號或雙引號中以 \u 開頭的都是 unicode 碼,unicode 碼對每個字符用4位16進制數表示。具體規則是:將一個字符(char)的高8位與低8位分別取出,轉化爲16進制數, 若是轉化的16進制數的長度不足2位,則在其後補0,而後將高、低8位轉成的16進制字符串拼接起來並在前面補上 "\u" 。 ip
漢子 '中' 由 unicode 編碼轉爲 gbk 編碼後,顯示的 gbk 編碼爲 'c8cb',和圖中的 '484b' 便不符合,這是由於GBK 的編碼爲了兼容 ASCII,即若是是英文,就用一個字節表示,2個字節就是中文,若是1個字節的第一位(最左邊一位)是0表示是 ASCII,若是連續的2個字節的第一位都是1,那麼這2個字節表示爲一箇中文,因此這裏的 'c8cb' 去掉首位的1後,就是 '484B' ~內存
ASCII 使用一個字節表示一個字符,而 unicode 須要2個字節,這樣對於英文的文本而言,存儲空間就多了一倍,因而就有了 UTF-8(可變長存儲,Unicode Transformation Format),UTF-8 簡稱萬國碼,能夠統一顯示中文簡體繁體及其它語言(如英文,日文,韓文)。UTF-8 編碼中英文字符只使用1字節表示,中文字符用3字節,其餘生僻字使用更多的字節存儲~
在內存中的字符統一使用 unicode 編碼,這樣能夠避免亂碼問題,當數據須要存儲到硬盤或者在網絡之間進行傳遞時,再將 unicode 編碼轉爲 其餘編碼標準(如今大多數狀況都是UTF-8,也推薦使用UTF-8),由於這樣更節省空間,也能夠減少網絡的傳輸壓力。
當數據須要從新讀入內存,就須要經過解碼轉爲 unicode,以前使用何種方式編碼存放到磁盤,就須要使用同一種編碼標準進行解碼。大體過程以下(UTF-8):
unicode(內存) -----> encode 編碼 -------->utf-8(磁盤) utf-8(磁盤) --------> decode 解碼 ---------->unicode(內存)
這樣也許有人會問,爲何內存中不直接使用 utf-8 編碼標準,utf-8 中囊括了全部語言,那是由於如今不少軟件還在使用各國的編碼標準(例如Shift_JIS,GBK,Euc-kr的等),utf-8 編碼標準中不存在 和這些編碼的映射關係(簡而言之就是 Shift_JIS,GBK,Euc-kr等這些編碼沒法轉換爲 utf-8編碼,utf-8編碼也不能轉換爲這些編碼),而 unicode 中存在,因此內存中一概使用 unicode 編碼標準能夠避免亂碼問題。utf-8 的出現主要是爲了減少存儲的空間,若是哪一天全部的軟件都是使用 utf-8 編碼標準,那數據讀取到內存中也不須要再轉爲 unicode。
常見的編碼問題通常有2種:
--第一種狀況
在數據存儲時,編碼錯誤。示例以下,文本中既有中文又有韓語,可是在存儲時使用 Euc-kr 編碼標準
保存後從新打開:
出現如上狀況的問題在於,在使用Euc-kr 編碼存儲時就已經發生了錯誤,韓文使用 Euc-kr 編碼(unicode --> Euc-kr)沒有問題,可是中文這個過程沒法完成,致使編碼失敗,數據沒法恢復~
--第二種狀況
數據正確編碼後存儲,在讀取時使用了錯誤的編碼
文本中只有韓文,且使用 Euc-kr 編碼後保存
從新 使用其餘編碼標準 解碼後打開
出現了亂碼,文本編碼後存儲沒有問題,可是在打開文件時使用了錯誤的解碼方式。這裏只要調整解碼方式就能夠,不會致使數據丟失!
從新設置成 Euc-kr 就能夠
總結:
一、內存中字符的存儲都是使用 unicode 編碼,在寫入到磁盤或者進行網絡傳輸時纔會將 unicode 編碼轉換成其餘編碼標準
二、在寫入到磁盤上或者進行網絡傳輸時,使用什麼編碼標準 進行編碼,以後就須要使用一樣的編碼標準進行解碼
三、推薦使用 utf-8 編碼標準,多國的文字能夠同時存在於一個文本中~
執行python程序,首先會啓動 python解釋器,而後 python解釋器會以 py文件 最前面2行指定的編碼方式來將py文件的內容讀入內存,定義編碼經常使用的方式以下:
1)# coding=<encoding name> 2)# -*- coding: <encoding name> -*-
如上語句必須放在py文件的第一行或者第二行~
若py文件中不指定編碼方式,則 python2 默認使用 ASCII,python3 默認使用 UTF-8。這個能夠經過sys.getdefaultencoding() 查看
python2環境
luyideMacBook-Pro:~ baby$ python Python 2.7.10 (default, Oct 6 2017, 22:29:07) ... >>> import sys >>> sys.getdefaultencoding() 'ascii'
python3環境
C:\Users\Baby>python Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:54:40)... >>> import sys >>> sys.getdefaultencoding() 'utf-8'
python解釋器加載代碼到內存後,代碼在內存中都是以 unicode 的格式存放的,可是當解釋器執行到存放字符串的語句時,例如:my_str = 'hello kitty',python解釋器會申請內存,而後將字符串編碼成 py文件開頭指定的編碼格式進行編碼,而後存放。python2中都是以上述過程存放字符串的;在python3中,字符串統一都是用 unicode 格式存放,python3中這樣作 避免了不少沒必要要的麻煩~~
--str類型
如上所說,python2中的字符串都是以 py文件開頭指定的編碼格式進行編碼後存放
python2環境
# -*- coding: utf-8 -*- my_str = '你好' print type(my_str) print (my_str,) 輸出結果: <type 'str'> ('\xe4\xbd\xa0\xe5\xa5\xbd',)
Tip:查看字符串在內存中存放的真實格式,能夠經過print元組或列表查看,直接print 會自動轉換編碼~
--unicode類型
python2環境
# -*- coding: utf-8 -*- my_str = u'你好' print type(my_str) print (my_str,) 輸出結果: <type 'unicode'> (u'\u4f60\u597d',)
Tip:字符串前面加個u,即字符串保存爲 unicode 格式~
unicode字符串 可經過編碼,轉換爲其餘編碼格式保存
# -*- coding: utf-8 -*- my_str = u'你好' print (my_str.encode('utf-8'),) 輸出結果: ('\xe4\xbd\xa0\xe5\xa5\xbd',)
在python3中,python解釋器將字符串保存至新申請的內存上,默認使用的就是unicode。
x = '你好' # 默認就使用unicode保存到內存中,前面無需加 u print(type(x)) # <class 'str'> y = x.encode('utf-8') print(y) # b'\xe4\xbd\xa0\xe5\xa5\xbd' print(type(y)) # <class 'bytes'> python3中申明 bytes 類型 x = bytes('abc'.encode('utf-8')) y = b'abc' print(type(x)) # <class 'bytes'> print(type(y)) # <class 'bytes'>
Tip:
1)python3中的字符串默認就是以unicode形式保存,與python2中的 x = u'你好' 語句相似
2)python3中的字符串 x = '你好' 使用 utf-8 編碼後輸出的結果是 b'\xe4\xbd\xa0\xe5\xa5\xbd',這與python2中的 "my_str = '你好';print ((my_str,))" (# -- coding: utf-8 --)輸出結果一致。.....python2中的 str 類型就是 python3 中的 bytes 類型~
查看python2中的 bytes 源代碼:
python2中的bytes是爲了兼容python3的寫法,python2 中 bytes 類型直接使用了str類型,因此:
python2中的字符串有3種類型:unicode、str、bytes,其中bytes和str爲同一個類型~
python3中的字符串有2種類型:str 和 bytes,str就是python2中的unicode,bytes就是python2中的str~
使用sys模塊中的getdefaultencoding可獲取python的默認編碼方式:
# python2 import sys print sys.getdefaultencoding() 輸出結果: ascii # python3 import sys print(sys.getdefaultencoding()) 輸出結果: utf-8
能夠看到python2中的默認編碼是ascii,python3中的是utf-8。字符串在encode和decode時(轉換爲unicode或從unicode轉爲其餘編碼)默認使用getdefaultencoding輸出的編碼格式,這個在python2中應用較多,python3中因爲字符串一概使用unicode存放,因此應用較少~
python3中,當str類型(unicode)和bytes類型合併時,會直接報錯
x = '你好,' # str類型 y = '貝貝'.encode('utf-8') # bytes類型 print(x + y) 報錯信息: TypeError: must be str, not bytes
可是在python2中,這個過程能夠進行,Python解釋器會自動把 str 轉換成 unicode 再進行運算,運算結果也都是 unicode類型,在Python解釋器自動將 str 轉成 unicode時,因爲沒有具體指定使用哪一種編碼進行轉碼,因此python解釋器就會默認使用 getdefaultencoding中的編碼,python2中默認編碼是ascii,因而就出現以下錯誤:
# -*- coding: utf-8 -*- x = u'你好,' y = '貝貝' print x + y 錯誤信息: UnicodeDecodeError: 'ascii' codec can't decode byte 0xe8 in position 0: ordinal not in range(128)
調整一下默認編碼,就能夠正常輸出:
# -*- coding: utf-8 -*- import sys reload(sys) sys.setdefaultencoding('utf-8') x = u'你好,' y = '貝貝' print x + y 輸出結果: 你好,貝貝
python2中字符串直接輸出到終端 和 字符串的編碼標準(以什麼標準編碼存放在內存上)即 終端編碼有關(例如windows終端編碼爲gbk,pycharm終端編碼爲utf-8),二者一致,才能避免亂碼
python3中有所區別,字符串默認使用unicode格式保存在內存中,這樣不管輸出到哪一個終端都不會有亂碼問題;若將字符串轉爲其餘編碼格式,則終端不會將其轉爲字符格式,而是原樣輸出
x = '你好' y = x.encode('utf-8') print(type(x)) print(x) print(type(y)) print(y) pycharm 輸出結果: <class 'str'> 你好 <class 'bytes'> b'\xe4\xbd\xa0\xe5\xa5\xbd'
二者輸出結果一致~.................^_^