Py 编码的真相
今天讓我們一起徹底揭開py編碼的真相,包括py2和py3。有同學可能問:以后py3是大勢所趨,還有必要了解py2那令人頭疼的編碼嗎?答案是太有必要啦。py2在生產中還是中流砥柱。
什么是編碼?
基本概念很簡單。首先,我們從一段信息即消息說起,消息以人類可以理解、易懂的表示存在。我打算將這種表示稱為“明文”(plain text)。對于說英語的人,紙張上打印的或屏幕上顯示的英文單詞都算作明文。
其次,我們需要能將明文表示的消息轉成另外某種表示,我們還需要能將編碼文本轉回成明文。從明文到編碼文本的轉換稱為“編碼”,從編碼文本又轉回成明文則為“解碼”。
...
py2編碼
str和unicode
str和unicode都是basestring的子類。嚴格意義上說,str其實是字節串,它是unicode經過編碼后的字節組成的序列。對UTF-8編碼的str'苑'使用len()函數時,結果是3,因為utf8編碼的'苑' ==?'\xe8\x8b\x91'。
而unicode是一個字符串,str是unicode這個字符串經過編碼(utf8,gbk等)后的字節組成的序列。如上面utf8編碼的字符串'漢'。
unicode才是真正意義上的字符串,對字節串str使用正確的字符編碼進行解碼后獲得,并且len(u'苑') == 1。
在Py2里,str=bytes。
py2編碼的最大特點是Python 2 將會自動的將bytes數據解碼成 unicode 字符串
所以在2里我們可以將字節與字符串拼接。
#coding:utf8print '苑昊' # 苑昊 print repr('苑昊')#'\xe8\x8b\x91\xe6\x98\x8a'print (u"hello"+"yuan")#print (u'苑昊'+'最帥') #UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6# in position 0: ordinal not in range(128)兩個問題:
1 print '苑昊'?:本來存的是'\xe8\x8b\x91\xe6\x98\x8a',為什么顯示了 苑昊 的明文?
2 字節串和字符串可以拼接?
這就是那些可惡的 UnicodeError 。你的代碼中包含了 unicode 和 byte 字符串,只要數據全部是 ASCII 的話,所有的轉換都是正確的,一旦一個非 ASCII 字符偷偷進入你的程序,那么默認的解碼將會失效,從而造成 UnicodeDecodeError 的錯誤。
Python 2 悄悄掩蓋掉了 byte 到 unicode 的轉換,讓程序在處理 ASCII 的時候更加簡單。你復出的代價就是在處理非 ASCII 的時候將會失敗。
再來看看encode()和decode()兩個basestring的實例方法,理解了str和unicode的區別后,這兩個方法就不會再混淆了:
| 1 2 3 4 5 6 7 8 9 10 11 | #coding:utf8 u?=?u'苑' print?repr(u)??# u'\u82d1' # print str(u)?? #UnicodeEncodeError s?=?u.encode('utf8') print?repr(s)?#'\xe8\x8b\x91' print?str(s)??#? 苑??? u2?=?s.decode('utf8') print?repr(u2)?# u'\u82d1' |
py3編碼
python3 renamed the unicode type to str ,the old str type has been replaced by bytes.
跟 Python 2 類似,Python 3 也有兩種類型,一個是 Unicode,一個是 byte 碼。但是他們有不同的命名。
現在你從普通文本轉換成 “str” 類型后存儲的是一個 unicode, “bytes” 類型存儲的是 byte 串。你也可以通過一個 b 前綴來制造 byte 串。
Python 3最重要的新特性大概要算是對文本和二進制數據作了更為清晰的區分。文本總是Unicode,由str類型表示,二進制數據則由bytes類型表示。Python 3不會以任意隱式的方式混用str和bytes,正是這使得兩者的區分特別清晰。你不能拼接字符串和字節包,也無法在字節包里搜索字符串(反之亦然),也不能將字符串傳入參數為字節包的函數(反之亦然)。這是件好事。
Python 3 中對 Unicode 支持的最大變化就是將會沒有對 byte 字節串的自動解碼。如果你想要用一個 byte 字節串和一個 unicode 相鏈接的話,你將會得到一個錯誤,不管你包含的內容是什么。
所有這些在 Python 2 中都將會有隱式的處理,而在 Python 3 中你將會得到一個錯誤。
| 1 2 | #print('alvin'+u'yuan')#字節串和unicode連接 py2:alvinyuan print(b'alvin'+'yuan')#字節串和unicode連接 py3:報錯 can't concat bytes to str |
轉換:
import jsons='苑昊' print(json.dumps(s)) #"\u82d1\u660a" b1=s.encode('utf8')print(b1,type(b1)) #b'\xe8\x8b\x91\xe6\x98\x8a' <class 'bytes'>print(b1.decode('utf8'))#苑昊 # print(b1.decode('gbk'))# 鑻戞槉b2=s.encode('gbk') print(b2,type(b2)) #'\xd4\xb7\xea\xbb' <class 'bytes'> print(b2.decode('gbk')) #苑昊注意:無論py2,還是py3,與明文直接對應的就是unicode數據,打印unicode數據就會顯示相應的明文(包括英文和中文)
編碼實現
說到編碼,我們需要在全局掌握這個工作過程,比如我們在pycharm上編寫一個.py文件,從保存到運行數據到底是怎么轉換的呢?
在解決這個問題之前,我們需要解決一個問題:默認編碼
默認編碼
什么是默認編碼?其實就是你的解釋器解釋代碼時默認的編碼方式,在py2里默認的編碼方式是ASCII,在py3里則是utf8(sys.getdefaultencoding()查看)。
| 1 | #-*- coding: UTF-8 -*- |
這個聲明是做什么的?我們在最開始只知道在py2里如果不加上這么一句話,程序一旦出現中文就會報錯,其實就是因為py2默認的ASCII碼,對于中文這些特殊字符無法編碼;
聲明這句話就是告訴python2.7解釋器 (默認ACSII編碼方式)解釋hello.py文件聲明下面的內容按utf8編碼,對,就是編碼(編碼成字節串最后轉成0101的形式讓機器去執行)
大家注意hello.py文件保存時有自己特定的編碼方式,比如utf8,比如gbk。
需要注意的是聲明的編碼必須與文件實際保存時用的編碼一致,否則很大幾率會出現代碼解析異常?,F在的IDE一般會自動處理這種情況,改變聲明后同時換成聲明的編碼保存,但文本編輯器控們需要小心。所以,保存的編碼樣式取決于你的編輯器默認的樣式(可調)。
文件保存和執行過程
我們講過,字符串在內存中是以unicode的數據形式保存的,可什么時候我們數據是在內存呢?讓我們一起解析這個過程
比如我們在pycharm上(py3.5)創建一個hello.py文件:
| 1 | print('hello 苑昊') |
這個時候我們的數據在內存嗎?NO,它已經被pycharm以默認的文件保存編碼方式存到了硬盤(二進制數據),所以一定注意,你點擊運行的時候,其實首先需要打開這個文件,然后將所有的數據轉移到內存,字符串此時就以unicode的數據格式存到內存的某塊地址上(為什么要這樣處理一會講到),其它內容還是utf8的編碼方式,然后解釋器就可以按著默認的utf8的編碼方式逐行解釋了?!?/p>
所以,一旦你的文件保存時的編碼與解釋器解釋的編碼不一致時就會出現錯誤。
print 都做了什么?
在Python 2中,print是一個語句(statement);而在Python 3中變成了函數(function)。
print語句
在Python 2中,print語句最簡單的使用形式就是print A,這相當于執行了:
| 1 | sys.stdout.write(str(A)?+?'\n') |
如果你以逗號為分隔符,傳遞額外的參數(argument),這些參數會被傳遞至str()函數,最終打印時每個參數之間會空一格。
# print A, B, C相當于sys.stdout.write(' '.join(map(str, [A, B, C])) + '\n')。如果print語句的最后再加上一個逗號,那么就不會再添加斷行符(\n),也就是說:# print A 相當于sys.stdout.write(str(A))print函數
import sysdef print(*objects, sep=None, end=None, file=None, flush=False):"""A Python translation of the C code for builtins.print()."""if sep is None:sep = ' 'if end is None:end = '\n'if file is None:file = sys.stdoutfile.write(sep.join(map(str, objects)) + end)if flush:file.flush()從上面的示例代碼中我們就可以看出,使用print函數有明顯的好處:與使用print語句相比,我們現在能夠指定其他的分隔符(separator)和結束符(end string)。
因為我們的目標是編碼,所以print函數的好處我們在這就不提了。
所以,無論2或3,對于print我們需要明晰一個方法:str()
py2:str()
# class str(object='') # Return a string containing a nicely printable representation of an object. For # strings, this returns the string itself. The difference with repr(object) is that # str(object) does not always attempt to return a string that is acceptable to # eval(); its goal is to return a printable string. If no argument is given, # returns the empty string, ''.# For more information on strings see Sequence Types — str, unicode, list, tuple, # bytearray, buffer, xrange which describes sequence functionality (strings are # sequences), and also the string-specific methods described in the String Methods # section. To output formatted strings use template strings or the % operator described # in the String Formatting Operations section. In addition see the String Services # section. See also unicode().py3:str()
# class str(object=b'', encoding='utf-8', errors='strict') # Return a string version of object. If object is not provided, returns the empty string. Otherwise, the behavior of str() depends on # whether encoding or errors is given, as follows. # # If neither encoding nor errors is given, str(object) returns object.__str__(), which is the “informal” or nicely printable string # representation of object. For string objects, this is the string itself. If object does not have a __str__() method, then str() falls # back to returning repr(object). # # If at least one of encoding or errors is given, object should be a bytes-like object (e.g. bytes or bytearray). In this case, if object # is a bytes (or bytearray) object, then str(bytes, encoding, errors) is equivalent to bytes.decode(encoding, errors). Otherwise, the bytes # object underlying the buffer object is obtained before calling bytes.decode(). See Binary Sequence Types — bytes, bytearray, memoryview # and Buffer Protocol for information on buffer objects. # # Passing a bytes object to str() without the encoding or errors arguments falls under the first case of returning the informal string # representation (see also the -b command-line option to Python). For example:常見編碼錯誤
1 cmd下的亂碼問題
hello.py
#coding:utf8print ('苑昊')文件保存時的編碼也為utf8。
思考:為什么在IDE下用2或3執行都沒問題,在cmd.exe下3正確,2亂碼呢?
我們在win下的終端即cmd.exe去執行,大家注意,cmd.exe本身就是一個軟件;當我們python2 hello.py時,python2解釋器(默認ASCII編碼)去按聲明的utf8編碼文件,而文件又是utf8保存的,所以沒問題;問題出在當我們print'苑昊'時,解釋器這邊正常執行,也不會報錯,只是print的內容會傳遞給cmd.exe顯示,而在py2里這個內容就是utf8編碼的字節數據,而這個軟件默認的編碼解碼方式是GBK,所以cmd.exe用GBK的解碼方式去解碼utf8自然會亂碼。
py3正確的原因是傳遞給cmd的是unicode數據,符合ISO統一標準的,所以沒問題。
| 1 | print?(u'苑昊') |
改成這樣后,cmd下用2也不會有問題了?!?/p>
?
2 ?print問題
在py2里
| 1 2 3 | #coding:utf8 print?('苑昊')?#苑昊 print?(['苑昊','yuan'])?#['\xe8\x8b\x91\xe6\x98\x8a', 'yuan'] |
在py3里
| 1 2 | print?('苑昊')?#苑昊 print?(['苑昊','yuan'])?#['苑昊', 'yuan'] |
?
轉載于:https://www.cnblogs.com/zxfprogram/p/8250389.html
總結
- 上一篇: Unity3D学习(七):Unity多重
- 下一篇: bzoj千题计划207:bzoj1879