程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Python >> 19.python的編碼問題

19.python的編碼問題

編輯:Python

  在正式說明之前,先給大家一個參考資料:戳這裡

  文章的內容參考了這篇資料,並加以總結,為了避免我總結的不夠完善,或者說出現什麼錯誤的地方,有疑問的地方大家可以看看上面那篇文章。

  下面開始講python中的編碼問題,首先,我們看看編碼有哪些。

  1. ASCII

  ASCII是用一個字節表示字符,而一個字節由八位二進制組成,所以能產生2**8=256種變化,在計算機剛誕生的年代,用來表示大小寫的26個英文字母,外加一些符號之類的還是綽綽有余的。這也是python2.x中默認使用的編碼,所以在python2.x中默認不能使用中文,除非使用編碼聲明。

  2. MBCS

  隨著時代的發展,ASCII就太夠用了,計算機不能只顯示英文吧,那樣實在太low。此時,大家看到ASCII碼中還有沒用完的,所以都想占用剩下的部分,但是剩下的部分還是不夠,例如我們中文那麼多肯定是不夠用的,所以此時又擴展了一下,一個字節不行,我就用兩個。而又為了兼容ASCII碼,有定義了這樣一套規則:如果第一個字節是\x80以下,則仍然表示ASCII字符;而如果是\x80以上,則跟下一個字節一起(共兩個字節)表示一個字符,然後跳過下一個字節,繼續往下判斷。例如GB...和BIG...之類的都遵循這樣的規則。

  但是,這樣還是實在太亂了,此時IBM跳了出來,大喊一聲:這些東西都要統一進行管理!!所以弄出了代碼頁的概念,將這些字符集都收錄了起來,並進行了分頁,而這些分頁的總稱就叫MBCS,例如GBK在936頁,所以又叫cp936。而大家都是使用的雙字節,所以也稱為DBCS。

  但很明顯,MBCS裡面收集和各樣的字符集,但是你不能說你要使用MBCS這個字符集編碼,裡面存了怎麼多種,到底是要用哪種,你不說清楚我總不能隨機給你一種吧。所以必須要進行指定,但是這個工作已經由操作系統自己完成了(linux不支持),而操作系統有時根據地區的不同而選擇的。例如簡體中文版的,就選GBK,其他國家的又會有不同,具體按版本而定。所以,一旦在python的編碼聲明中使用MBCS/BDCS,在進行過系統或跨地區運行的時候,報錯也是在所難免的。所以編碼聲明中一定要具體的指定,例如我們常用的utf-8,這樣就不會因為系統和地區的差異而造成各種編碼的錯誤。

  在windows中,微軟又為它起了個別名,叫ANSI,其實就是MBSC,大家知道就好了。

  3.Unicode

  雖然MBSC一定程度上解決了編碼混亂的問題,但還是特點的編碼只能顯示特點的字符。這樣要開發一種適配多國語言的程序就變得非常困難,此時人們在想,有沒有一種編碼能搞到所以的字符。大家研究了一番之後,Unicode就此誕生。干脆大家都不要在ASCII上拓展來拓展去,搞得各種版本如此混亂。以後大家都用兩個字節保存算了,這樣就有了256*256=65536種字符可以表示了,總歸是夠用了吧。這就是UCS-2標准了。後來還有人說不夠用的,那麼干脆翻一倍,用四個字節表示,256**4=4294967296,就算將來表示外星文字也能撐一段時間了吧。當然現在常用的還是UCS-2標准的。

  UCS(Unicode Character Set)還僅僅是字符對應碼位的一張表而已(也就是表示字節),比如"漢"這個字的碼位是6C49。字符具體如何傳輸和儲存則是由UTF(UCS Transformation Format)來負責(也就是保存字節)。(注意:表示字節≠保存字節,也就是雖然我用了2個字節表示字符,但是我保存的時候不一定就直接保存用來表示的那個字節)

  剛開始都是直接使用UCS的碼位來保存,這就是UTF-16,比如,"漢"直接使用\x6C\x49保存(UTF-16-BE),或是倒過來使用\x49\x6C保存(UTF-16-LE)。但美國佬後來不願意了,我原來用ASCII只有1個字節就能搞到,現在卻要兩個字節,足足長了一倍呀。一倍是什麼概念,四捨五入那是將近一個億呀。真當我磁盤空間不用錢呀,為了滿足這個述求,就誕生了UTF-8。

  UTF-8是一種很別扭的編碼,具體表現在他是變長的,並且兼容ASCII,ASCII字符使用1字節表示。但有得必有失,在UTF-8中,東亞的文字是用三個字節表示的,包括中文,一些不常用的字符更是用四個字節表示。於是別的國家保存的成本變高了,而美國佬卻變低了。又再次坑了別人,滿足了自己。但是沒辦法,誰叫人家是計算機界的老大呢?

 

什麼是BOM

   當一個文本編輯要打開一個文件時,它表示懵逼了。世間編碼如此之多,我究竟要用什麼哪種編碼去解碼呀?你總得告訴我吧!

  此時,UTF就進入了BOM來表示編碼。所謂的BOM就是文件使用編碼的標識符,就和python的編碼聲明一樣,告訴文本編輯器我用的是什麼編碼,下面的你都用那個編碼去解碼就行。

  同樣的,只有文本編輯器在文件開頭的地方讀到了關於BOM的描述,就能夠進行正確的界面了。

  下面是一些BOM的總結:

  BOM_UTF8 '\xef\xbb\xbf'
  BOM_UTF16_LE '\xff\xfe'
  BOM_UTF16_BE '\xfe\xff'

  同樣了,為了我們自己編輯的文件的編碼也能被正確識別,我們也要寫入BOM,一般由編輯器完成。但不寫也可以,只有在打開文件的時候自己手動選擇用什麼去解碼也是可以的。

  但是,還有一種叫UTF-8無BOM模式的,這又是什麼鬼。

  因為UTF-8實在太流行了,所以文本編輯器默認會首先用UTF-8進行解碼。即使是保存時默認使用ANSI(MBCS)的記事本,在讀取文件時也是先使用UTF-8測試編碼,如果可以成功解碼,則使用UTF-8解碼。記事本這個別扭的做法造成了一個BUG:如果你新建文本文件並輸入"姹塧"然後使用ANSI(MBCS)保存,再打開就會變成"漢a"。)

  下用一幅圖來總結:

  此時,有些人會在MBCS和UCS-2之間迷糊,大家都是兩個字節表示,又有什麼不同?

  MBCS是各自拓展的,有就是說很可能相同的二進制表示MBCS會出現不同的結果,而Unicode是統一拓展,保證了每種二進制表示都對應唯一一個字符,保證了唯一性,也就提高了兼容能力。

 


   

  ok,在講完字符編碼的問題之後,現在再來看一下:

   # coding:gbk 和 # coding= utf-8 之類的編碼聲明對python而言到底意味著什麼。

  這裡插播一個小技巧:

   # coding : utf-8 或者這樣 # coding = utf-8 的聲明方式是會報錯的,這裡並不是說是特點的=或者:的問題,而是空格的問題,在coding和符號之間是不能有空格的,但在符號和utf-8之類的編碼名稱間是運行0個或多個空格的,#和coding間也是運行0個或多個空格的。我也不知道為什麼,但實際就是報錯了。

#! /usr/bin/env python
#coding = utf-8
print '中文'

 

  這裡coding和=號一個空格:

  報錯了。

#! /usr/bin/env python
#coding= utf-8
print '中文'

  coding和=之間沒空格:

  正常執行。

  不清楚是我IDE的問題還是python本來的語法是這樣規定的,但其實很少有地方談及這個地方的語法,所以這裡提及一下,最後自己實驗一下。

   # -*- coding: utf-8 -*- 的寫法也是一樣的。

 

  好了,一下進入正題:

#! /usr/bin/env python
# coding= utf-8
print '中文'
print str('中文')
print repr('中文')
print repr(u'中文')

 

  這裡順便解釋一下str()函數和repr()函數在創建或轉換字符串上的區別,str()得到是一個對人類可讀性比較好的字符串,而print也是默認調用這個函數的。而repr()是創建或轉換字符串時,得到是對機器可讀性更好的字符串,就是這裡得到是其編碼。

  下面是另一種編碼的輸出:

#! /usr/bin/env python
# coding= gbk
print '中文'
print str('中文')
print repr('中文')
print repr(u'中文')

 

 

  前面兩個是亂碼,不過這裡是我IDE的問題,我的IDE默認是用UTF-8的。

  這裡改成GBK再試試。

  又可以了。

  這裡再次引入一個問題,什麼是文件保存編碼,什麼是代碼運行編碼。

  還是不能,那再改一下。

  這樣又可以了。

  其實保存編碼,也就是IDE Encoding設置的是在磁盤中保存和打開的時候的編碼。假設我使用的是windows的文本編輯器寫的代碼,也就是用ANSI保存的,如何我再用其他編碼打開就會出現亂碼,這無關運行的事。而運行編碼是則是影響python交互界面的編碼,我在前面的python的第一個程序中也說過這個問題,我用windows的cmd運行一個python文件,為什麼我在python中聲明了可以使用中文的utf-8,卻在輸出顯示的時候還是出現了亂碼?這是因為雖然python輸出的utf-8的編碼,但是cmd這個家伙默認是用GBK去解碼的,所以出現了顯示上的亂碼。但這並不是真正的亂碼,換用一個支持utf-8的顯示環境就可以了。

 

  好,題外話多了些,但這些小的錯誤可能會讓人糾結很久都解決不了,所以這裡還是提及一下的好。

  下面進入正題,看下面的現象:

  utf-8:

  gbk:

  

  我們可以發現,隨著python編碼聲明的不同,其字符串的編碼也不同,所以我們得出一個結論:

  python中的編碼聲明影響的是普通字符串的編碼,也是就用工程函數str()或者單純的引號創建的出來的字符串。是隨著編碼聲明的不同而不同的。

  此時再看一下這個現象:

  發現Unicode字符串無論在上面情況下都是一樣的,因為它永遠采用的是Unicode進行編碼,不管你聲明的到底是什麼。

  Unicode字符串的創建,也就是Unicode對象的創建可以使用工廠函數unicode()或者在引號前面加個u。

  所以,我們可以得出一個在python進行編碼轉換的規則:

  再總體擴展一下:

  只要python支持的字符集,都能夠用這樣的邏輯在python的內部進行編碼轉換。

  只要知道了這些,那麼在文件處理中就能避免許多錯誤了,關於python的文件處理我們下篇再講。

  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved