1. 程式人生 > >[轉]py編碼終極版

[轉]py編碼終極版

讓我 也有 同時 世界 命令行 存儲 exe article 基本概念

py編碼終極版

原文鏈接:http://www.cnblogs.com/yuanchenqi/articles/5956943.html

一 什麽是編碼?

基本概念很簡單。首先,我們從一段信息即消息說起,消息以人類可以理解、易懂的表示存在。我打算將這種表示稱為“明文”(plain text)。對於說英語的人,紙張上打印的或屏幕上顯示的英文單詞都算作明文。

其次,我們需要能將明文表示的消息轉成另外某種表示,我們還需要能將編碼文本轉回成明文。從明文到編碼文本的轉換稱為“編碼”,從編碼文本又轉回成明文則為“解碼”。

編碼問題是個大問題,如果不徹底解決,它就會像隱藏在叢林中的小蛇,時不時地咬你一口。
    那麽到底什麽是編碼呢?

    
//ASCII 記住一句話:計算機中的所有數據,不論是文字、圖片、視頻、還是音頻文件,本質上最終都是按照類似 01010101 的二進制存儲的。 再說簡單點,計算機只懂二進制數字! 所以,目的明確了:如何將我們能識別的符號唯一的與一組二進制數字對應上?於是美利堅的同誌想到通過一個電平的高低狀態來代指0或1, 八個電平做為一組就可以表示出 256種不同狀態,每種狀態就唯一對應一個字符,比如A--->00010001,而英文只有26個字符,算上一些特殊字符和數字,128個狀態也夠 用了;每個電平稱為一個比特為,約定8個比特位構成一個字節,這樣計算機就可以用127個不同字節來存儲英語的文字了。這就是ASCII編碼。 擴展ANSI編碼 剛才說了,最開始,一個字節有八位,但是最高位沒用上,默認為0;後來為了計算機也可以表示拉丁文,就將最後一位也用上了, 從128到255的字符集對應拉丁文啦。至此,一個字節就用滿了!
//GB2312 計算機漂洋過海來到中國後,問題來了,計算機不認識中文,當然也沒法顯示中文;而且一個字節所有狀態都被占滿了,萬惡的帝國主義亡 我之心不死啊!我黨也是棒,自力更生,自己重寫一張表,直接生猛地將擴展的第八位對應拉丁文全部刪掉,規定一個小於127的字符的意 義與原來相同,但兩個大於127的字符連在一起時,就表示一個漢字,前面的一個字節(他稱之為高字節)從0xA1用到0xF7,後面一個字節 (低字節)從0xA1到0xFE,這樣我們就可以組合出大約7000多個簡體漢字了;這種漢字方案叫做 “GB2312”。GB2312 是對 ASCII 的中文擴展。 //GBK 和 GB18030編碼 但是漢字太多了,GB2312也不夠用,於是規定:只要第一個字節是大於127就固定表示這是一個漢字的開始,不管後面跟的是不是擴展字符集裏的 內容。結果擴展之後的編碼方案被稱為 GBK 標準,GBK 包括了 GB2312 的所有內容,同時又增加了近20000個新的漢字(包括繁體字)和符號。
//UNICODE編碼: 很多其它國家都搞出自己的編碼標準,彼此間卻相互不支持。這就帶來了很多問題。於是,國際標誰化組織為了統一編碼:提出了標準編碼準 則:UNICODE 。 UNICODE是用兩個字節來表示為一個字符,它總共可以組合出65535不同的字符,這足以覆蓋世界上所有符號(包括甲骨文) //utf8: unicode都一統天下了,為什麽還要有一個utf8的編碼呢? 大家想,對於英文世界的人們來講,一個字節完全夠了,比如要存儲A,本來00010001就可以了,現在吃上了unicode的大鍋飯, 得用兩個字節:00000000 00010001才行,浪費太嚴重! 基於此,美利堅的科學家們提出了天才的想法:utf8. UTF-8(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字符編碼,它可以使用1~4個字節表示一個符號,根據 不同的符號而變化字節長度,當字符在ASCII碼的範圍時,就用一個字節表示,所以是兼容ASCII編碼的。 這樣顯著的好處是,雖然在我們內存中的數據都是unicode,但當數據要保存到磁盤或者用於網絡傳輸時,直接使用unicode就遠不如utf8省空間啦! 這也是為什麽utf8是我們的推薦編碼方式。 Unicode與utf8的關系: 一言以蔽之:Unicode是內存編碼表示方案(是規範),而UTF是如何保存和傳輸Unicode的方案(是實現)這也是UTF與Unicode的區別。

補充:utf8是如何節約硬盤和流量的

s="I‘m 苑昊"

 你看到的unicode字符集是這樣的編碼表:

I  0049
  0027
m  006d
   0020
苑 82d1
昊 660a

這個字符串總共占用了12個字節,但是對比中英文的二進制碼,可以發現,英文前9位都是0!浪費啊,浪費硬盤,浪費流量。怎麽辦?UTF8:

I    01001001
    00100111
m    01101101
     0010000011101000 10001011 1001000111100110 10011000 10001010

utf8用了10個字節,對比unicode,少了兩個,因為我們的程序英文會遠多於中文,所以空間會提高很多!

記住:一切都是為了節省你的硬盤和流量。

二 py2的string編碼

在py2中,有兩種字符串類型:str類型和unicode類型;註意,這僅僅是兩個名字,python定義的兩個名字,關鍵是這兩種數據類型在程序運行時存在內存地址的是什麽?

我們來看一下:

#coding:utf8
 
s1=‘苑‘
 
print type(s1) # <type ‘str‘>
print repr(s1) #‘\xe8\x8b\x91
 
s2=u‘苑‘
print type(s2) # <type ‘unicode‘>
print repr(s2) # u‘\u82d1‘

 內置函數repr可以幫我們在這裏顯示存儲內容。原來,str和unicode分別存的是字節數據和unicode數據;那麽兩種數據之間是什麽關心呢?如何轉換呢?這裏就涉及到編碼(encode)和解碼(decode)了

s1=u
print repr(s1) #u‘\u82d1‘

b=s1.encode(utf8)
print b
print type(b)  #<type ‘str‘>
print repr(b)  #‘\xe8\x8b\x91‘

s2=苑昊
u=s2.decode(utf8)
print u        # 苑昊
print type(u)  # <type ‘unicode‘>
print repr(u)  # u‘\u82d1\u660a‘

#註意
u2=s2.decode(gbk)
print u2  #鑻戞槉

print len(苑昊) #6

無論是utf8還是gbk都只是一種編碼規則,一種把unicode數據編碼成字節數據的規則,所以utf8編碼的字節一定要用utf8的規則解碼,否則就會出現亂碼或者報錯的情況。

技術分享

py2編碼的特色:

#coding:utf8
 
print ‘苑昊‘ #  苑昊   
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)

 Python 2 悄悄掩蓋掉了 byte 到 unicode 的轉換,只要數據全部是 ASCII 的話,所有的轉換都是正確的,一旦一個非 ASCII 字符偷偷進入你的程序,那麽默認的解碼將會失效,從而造成 UnicodeDecodeError 的錯誤。py2編碼讓程序在處理 ASCII 的時候更加簡單。你復出的代價就是在處理非 ASCII 的時候將會失敗。

三 py3的string編碼

python3 renamed the unicode type to str ,the old str type has been replaced by bytes.

py3也有兩種數據類型:str和bytes; str類型存unicode數據,bytse類型存bytes數據,與py2比只是換了一下名字而已。

import json

s=苑昊
print(type(s))       #<class ‘str‘>
print(json.dumps(s)) #  "\u82d1\u660a"

b=s.encode(utf8)
print(type(b))      # <class ‘bytes‘>
print(b)            # b‘\xe8\x8b\x91\xe6\x98\x8a‘


u=b.decode(utf8)
print(type(u))       #<class ‘str‘>
print(u)             #苑昊
print(json.dumps(u)) #"\u82d1\u660a"


print(len(苑昊)) # 2

技術分享

py3的編碼哲學:

Python 3最重要的新特性大概要算是對文本和二進制數據作了更為清晰的區分,不再會對bytes字節串進行自動解碼。文本總是Unicode,由str類型表示,二進制數據則由bytes類型表示。Python 3不會以任意隱式的方式混用str和bytes,正是這使得兩者的區分特別清晰。你不能拼接字符串和字節包,也無法在字節包裏搜索字符串(反之亦然),也不能將字符串傳入參數為字節包的函數(反之亦然)。

#print(‘alvin‘+u‘yuan‘)#字節串和unicode連接 py2:alvinyuan
print(b‘alvin‘+‘yuan‘)#字節串和unicode連接 py3:報錯 can‘t concat bytes to str

註意:無論py2,還是py3,與明文直接對應的就是unicode數據,打印unicode數據就會顯示相應的明文(包括英文和中文)

四 文件從磁盤到內存的編碼(******)

說到這,才來到我們的重點!

拋開執行執行程序,請問大家,文本編輯器大家都是用過吧,如果不懂是什麽,那麽word總用過吧,ok,當我們在word上編輯文字的時候,不管是中文還是英文,計算機都是不認識的,那麽在保存之前數據是通過什麽形式存在內存的呢?yes,就是unicode數據,為什麽要存unicode數據,這是因為它的名字最屌:萬國碼!解釋起來就是無論英文,中文,日文,拉丁文,世界上的任何字符它都有唯一編碼對應,所以兼容性是最好的。

好,那當我們保存了存到磁盤上的數據又是什麽呢?

答案是通過某種編碼方式編碼的bytes字節串。比如utf8---一種可變長編碼,很好的節省了空間;當然還有歷史產物的gbk編碼等等。於是,在我們的文本編輯器軟件都有默認的保存文件的編碼方式,比如utf8,比如gbk。當我們點擊保存的時候,這些編輯軟件已經"默默地"幫我們做了編碼工作。

那當我們再打開這個文件時,軟件又默默地給我們做了解碼的工作,將數據再解碼成unicode,然後就可以呈現明文給用戶了!所以,unicode是離用戶更近的數據,bytes是離計算機更近的數據。

說了這麽多,和我們程序執行有什麽關系呢?

先明確一個概念:py解釋器本身就是一個軟件,一個類似於文本編輯器一樣的軟件!

現在讓我們一起還原一個py文件從創建到執行的編碼過程:

打開pycharm,創建hello.py文件,寫入

ret=1+1
s=苑昊
print(s)
print(s)

當我們保存的的時候,hello.py文件就以pycharm默認的編碼方式保存到了磁盤;關閉文件後再打開,pycharm就再以默認的編碼方式對該文件打開後讀到的內容進行解碼,轉成unicode到內存我們就看到了我們的明文;

而如果我們點擊運行按鈕或者在命令行運行該文件時,py解釋器這個軟件就會被調用,打開文件,然後解碼存在磁盤上的bytes數據成unicode數據,這個過程和編輯器是一樣的,不同的是解釋器會再將這些unicode數據翻譯成C代碼再轉成二進制的數據流,最後通過控制操作系統調用cpu來執行這些二進制數據,整個過程才算結束。

那麽問題來了,我們的文本編輯器有自己默認的編碼解碼方式,我們的解釋器有嗎?

當然有啦,py2默認ASCII碼,py3默認的utf8,可以通過如下方式查詢

import sys
print(sys.getdefaultencoding())

 大家還記得這個聲明嗎?

#coding:utf8

 

是的,這就是因為如果py2解釋器去執行一個utf8編碼的文件,就會以默認地ASCII去解碼utf8,一旦程序中有中文,自然就解碼錯誤了,所以我們在文件開頭位置聲明 #coding:utf8,其實就是告訴解釋器,你不要以默認的編碼方式去解碼這個文件,而是以utf8來解碼。而py3的解釋器因為默認utf8編碼,所以就方便很多了。

技術分享

註意:我們上面講的string編碼是在cpu執行程序時的存儲狀態,是另外一個過程,不要混淆!

五 常見的編碼問題

1 cmd下的亂碼問題

hello.py

#coding:utf8
print (‘苑昊‘)

 

文件保存時的編碼也為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數據,cmd.exe可以識別內容,所以顯示沒問題。

明白原理了,修改就有很多方式,比如:

print (u‘苑昊‘)

改成這樣後,cmd下用2也不會有問題了。

2 open()中的編碼問題

創建一個hello文本,保存成utf8:

苑昊,你最帥!

同目錄下創建一個index.py

f=open(hello)
print(f.read())

為什麽 在linux下,結果正常:苑昊,在win下,亂碼:鑻戞槉(py3解釋器)?

因為你的win的操作系統安裝時是默認的gbk編碼,而linux操作系統默認的是utf8編碼;

當執行open函數時,調用的是操作系統打開文件,操作系統用默認的gbk編碼去解碼utf8的文件,自然亂碼。

解決辦法:

f=open(hello,encoding=utf8)
print(f.read())

如果你的文件保存的是gbk編碼,在win 下就不用指定encoding了。

另外,如果你的win上不需要指定給操作系統encoding=‘utf8‘,那就是你安裝時就是默認的utf8編碼或者已經通過命令修改成了utf8編碼。

註意:open這個函數在py2裏和py3中是不同的,py3中有了一個encoding=None參數

[轉]py編碼終極版