python2和python3編碼解碼詳解

來源:互聯網
上載者:User

標籤:英語   write   錯誤   記憶體   中文   key   .exe   error   alt   

今天讓我們一起徹底揭開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的區別後,這兩個方法就不會再混淆了:

1234567891011 #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 中你將會得到一個錯誤。

12 #print(‘alvin‘+u‘yuan‘)#位元組串和unicode串連 py2:alvinyuanprint(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。

需要注意的是聲明的編碼必須與檔案實際儲存時用的編碼一致,否則很大幾率會出現代碼解析異常。現在的IDE一般會自動處理這種情況,改變聲明後同時換成聲明的編碼儲存,但文字編輯器控們需要小心。所以,儲存的編碼樣式取決於你的編輯器預設的樣式(可調)。

檔案儲存和執行過程

我們講過,字串在記憶體中是以unicode的資料形式儲存的,可什麼時候我們資料是在記憶體呢?讓我們一起解析這個過程

比如我們在pycharm上(py3.5)建立一個hello.py檔案:

1 print(‘hello 苑昊‘)

這個時候我們的資料在記憶體嗎?NO,它已經被pycharm以預設的檔案儲存編碼方式存到了硬碟(位元據),所以一定注意,你點擊啟動並執行時候,其實首先需要開啟這個檔案,然後將所有的資料轉移到記憶體,字串此時就以unicode的資料格式存到記憶體的某塊地址上(為什麼要這樣處理一會講到),其它內容還是utf8的編碼方式,然後解譯器就可以按著預設的utf8的編碼方式逐行解釋了。 

所以,一旦你的檔案儲存時的編碼與解譯器解釋的編碼不一致時就會出現錯誤。

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.stdout    file.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也不會有問題了。 

 

2  print問題

在py2裡

123 #coding:utf8print (‘苑昊‘) #苑昊print ([‘苑昊‘,‘yuan‘]) #[‘\xe8\x8b\x91\xe6\x98\x8a‘, ‘yuan‘]

在py3裡

12 print (‘苑昊‘) #苑昊print ([‘苑昊‘,‘yuan‘]) #[‘苑昊‘, ‘yuan‘]

 轉載於袁老師的部落格:http://www.cnblogs.com/yuanchenqi/articles/5938733.html

python2和python3編碼解碼詳解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.