python3.3 unicode(encode&decode)


最近在用python寫多語言的一個插件時,涉及到python3.x中的unicode和編碼操作,本文就是針對編碼問題研究的匯總,目前已開源至github。以下內容來自項目中的README。

1 ASCII、UNICODE、GBK、CP936、MSCS

1.1 ASCII

美國信息交換標准碼。 在計算機的存儲單元中,一個ASCII碼值占一個字節(8個二進制位),但其最高位(b7)用作奇偶校驗位。ASCII(American Standard Code for Information Interchange),是一種單字節的編碼。計算機世界里一開始只有英文,而單字節可以表示256個不同的字符,可以表示所有的英文字符和許多的控制符號。不過ASCII只用到了其中的一半(\x80以下),這也是MBCS得以實現的基礎。

1.2 ISO8859-1、EASCII

EASCII是ASCII的擴充,把第八位也用來存儲信息;在Windows中用Alt+小鍵盤數字輸入的就是EASCII碼對應字符。ISO8859-1就是EASCII最典型的實現,基本能夠覆蓋西歐的拉丁字母,所以又叫Latin-1。有些國外程序就要求使用ISO8859-1編碼以保證Binary Safe,比如著名的XMB。

1.3 Unicode、UTF-8

Unicode是業界的一種標准,它可以使電腦得以呈現世界上數十種文字的系統。 后來,有人開始覺得太多編碼導致世界變得過於復雜了,讓人腦袋疼,於是大家坐在一起拍腦袋想出來一個方法:所有語言的字符都用同一種字符集來表示,這就是Unicode。

最初的Unicode標准UCS-2使用兩個字節表示一個字符,所以你常常可以聽到Unicode使用兩個字節表示一個字符的說法。但過了不久有人覺得256*256太少了,還是不夠用,於是出現了UCS-4標准,它使用4個字節表示一個字符,不過我們用的最多的仍然是UCS-2。

UCS(Unicode Character Set)還僅僅是字符對應碼位的一張表而已,比如"漢"這個字的碼位是6C49。字符具體如何傳輸和儲存則是由UTF(UCS Transformation Format)來負責。

一開始這事很簡單,直接使用UCS的碼位來保存,這就是UTF-16,比如,"漢"直接使用\x6C\x49保存(UTF-16-BE),或是倒過來使用\x49\x6C保存(UTF-16-LE)。但用着用着美國人覺得自己吃了大虧,以前英文字母只需要一個字節就能保存了,現在大鍋飯一吃變成了兩個字節,空間消耗大了一倍……於是UTF-8橫空出世。

UTF-8是一種很別扭的編碼,具體表現在他是變長的,並且兼容ASCII,ASCII字符使用1字節表示。然而這里省了的必定是從別的地方摳出來的,你肯定也聽說過UTF-8里中文字符使用3個字節來保存吧?4個字節保存的字符更是在淚奔……(具體UCS-2是怎么變成UTF-8的請自行搜索) Unicode的實現方式不同於編碼方式,一個字符的Unicode編碼是確定的,但是在實際傳輸過程中,由於不同系統平台的設計不一定一致,以及出於節省空間的目的,對Unicode編碼的實現方式有所不同。於是就有了UTF-8、UTF-16、UTF-32。

UTF-8使用一至四個字節為每個字符編碼:

ASCII字符只需一個字節編碼(Unicode范圍由U+0000至U+007F)。帶有附加符號的拉丁文、希臘文、西里爾字母、亞美尼亞語、希伯來文、阿拉伯文、敘利亞文及它拿字母(即以ISO 8859為主的)則需要二個字節編碼(Unicode范圍由U+0080至U+07FF)。其他基本多文種平面(BMP)中的字符(這包含了大部分常用字,包括漢字)使用三個字節編碼。其他極少使用的Unicode 輔助平面的字符使用四字節編碼。它唯一的好處在於兼容ASCII。 UTF-16則是以U+10000為分界線,使用兩個字節或者四個字節存儲。 UTF-32則是全部使用4字節編碼,很浪費空間。

1.4 GB2312、GBK、GB18030

GB是中國荒謬的國家標准。GB2312、GBK、GB18030各為前一個的擴展。

我從來討厭GB編碼,因為它毫無國際兼容性。更荒謬的是,GBK和GB18030幾乎是照着Unicode字符集選取的字庫。這樣多此一舉地弄出一套編碼,還強制所有在中國銷售的操作系統必須使用它,真是天朝特色。

另外,對於GB編碼PHP是不認賬的,mb_detect_encoding函數會把GB編碼識別成CP936。

1.5 MSCS

然而計算機世界里很快就有了其他語言,單字節的ASCII已無法滿足需求。后來每個語言就制定了一套自己的編碼,由於單字節能表示的字符太少,而且同時也需要與ASCII編碼保持兼容,所以這些編碼紛紛使用了多字節來表示字符,如GBxxx、BIGxxx等等,他們的規則是,如果第一個字節是\x80以下,則仍然表示ASCII字符;而如果是\x80以上,則跟下一個字節一起(共兩個字節)表示一個字符,然后跳過下一個字節,繼續往下判斷。

這里,IBM發明了一個叫Code Page的概念,將這些編碼都收入囊中並分配頁碼,GBK是第936頁,也就是CP936。所以,也可以使用CP936表示GBK。

MBCS(Multi-Byte Character Set)是這些編碼的統稱。目前為止大家都是用了雙字節,所以有時候也叫做DBCS(Double-Byte Character Set)。必須明確的是,MBCS並不是某一種特定的編碼,Windows里根據你設定的區域不同,MBCS指代不同的編碼,而Linux里無法使用MBCS作為編碼。在Windows中你看不到MBCS這幾個字符,因為微軟為了更加洋氣,使用了ANSI來嚇唬人,記事本的另存為對話框里編碼ANSI就是MBCS。同時,在簡體中文Windows默認的區域設定里,指代GBK。

2 open函數

open狀態rb對應的是_io.BufferedReader,r對應的是_io.TextIOWrapper

class io.TextIOWrapper(buffer, encoding=None, errors=None, newline=None, 
line_buffering=False)

A buffered text stream over a BufferedIOBase binary stream. It inherits TextIOBase. encoding gives the name of the encoding that the stream will be decoded or encoded with. It defaults to locale.getpreferredencoding().

3 encode和decode方法

字符串在Python內部的表示是unicode編碼,因此,在做編碼轉換時,通常需要以unicode作為中間編碼,即先將其他編碼的字符串解碼(decode)成unicode,再從unicode編碼(encode)成另一種編碼decode的作用是將其他編碼的字符串轉換成unicode編碼,如str1.decode('gb2312'),表示將gb2312編碼的字符串str1轉換成unicode編碼。 encode的作用是將unicode編碼轉換成其他編碼的字符串,如str2.encode('gb2312'),表示將unicode編碼的字符串str2轉換成gb2312編碼。

因此,轉碼的時候一定要先搞明白,字符串str是什么編碼,然后decode成unicode,然后再encode成其他編碼。代碼中字符串的默認編碼與代碼文件本身的編碼一致。

如:s='中文' 如果是在utf8的文件中,該字符串就是utf8編碼,如果是在gb2312的文件中,則其編碼為gb2312。這 種情況下,要進行編碼轉換,都需要先用decode方法將其轉換成unicode編碼,再使用encode方法將 其轉換成其他編碼。通常,在沒有指定特定的編碼方式時,都是使用的系統默認編碼創建的代碼文件

4 相關代碼

python默認編碼

    default encodings in Python are:
    Python 2.x: ASCII
    Python 3.x: UTF-8

win7中文環境中對應的系統參數

    print('<strong>python系統參數:')
    print(locale.getdefaultlocale()) #('zh_CN', 'cp936')
    print(locale.getpreferredencoding()) # cp936
    print(sys.getdefaultencoding()) #utf-8
    print(sys.getfilesystemencoding())#mbcs
    print(sys.maxunicode)
    print(codecs.lookup('utf-8'))#codeinfo class

    ('zh_CN', 'cp936')
    cp936
    utf-8
    mbcs
    1114111

utf-8, gbk codecs error

    ch_str = '中文'
    try:
        codecs_decode(codecs_encode(ch_str,'gbk'))
    except Exception:
        print('<strong>utf-8 codec decode error')

    codecs_decode(codecs_encode('1ère Recuérdame écouteur ça'))
    codecs_decode(codecs_encode('1ère Recuérdame écouteur ça'),'gbk')
    try:
        codecs_decode(codecs_encode('1ère','gbk'))
    except Exception:
        print('<strong>utf-8 codec decode error')

    code_str = '中國'
    print(code_str.encode().decode())
    print(code_str.encode().decode('mbcs','ignore'))
    try:
        print(code_str.encode().decode('gbk','strict'))
    except Exception:
        print('<strong>gbk codec decode error')

binary寫文件


#write french in file
def write_file(filename):
    with open(filename,'wb') as file:
        file.write('ry dialect: /a/, /ɑ/, /e/, /ɛ/, /ə/, /i/, /o/, /ɔ/, /'.encode())

def write_file_append(filename,string):
    line_list = []
    with open(filename,'rb') as file:
        for line in file:
            line_list.append(line)

    with open(filename,'wb') as file:
        for i in range(len(line_list)):
            file.write(line_list[i])

        file.write(string.encode())

5 參考資料

PEP and ISSUES:


  • ISSUES:

distutils.commands.bdist_wininst.bdist_wininst.get_inidata use mdcs encodinghttp://bugs.python.org/issue10945

bytes.decode('mbcs', 'ignore') does replace undecodable bytes on Windows Vista or laterhttp://bugs.python.org/issue12281

  • PEP393:

Flexible String Representation http://www.python.org/dev/peps/pep-0393/#discussion

PEP0263: Defining Python Source Code Encodings http://www.python.org/dev/peps/pep-0263/

p.s.


  • sublime3/2的錯誤編碼

(由於sublime自身的編碼原因,所以可能造成在ctrl+b編譯的過程中,對多語言的編碼出現錯誤)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM