1.Python中文處理
轉自:http://bbs.chinaunix.net/thread-1431029-1-1.html
Python的中文處理一、使用中文字元 在python源碼中如果使用了中文字元,運行時會有錯誤,解決的辦法是在源碼的開頭部分加入字元編碼的聲明,下面是一個例子: #!/usr/bin/env python # -*- coding: cp936 -*- Python Tutorial中指出,python的源檔案可以編碼ASCII以外的字元集,最好的做法是在#!行後面用一個特殊的注釋行來定義字元集: # -*- coding: encoding -*-根據這個聲明,Python會嘗試將檔案中的字元編碼轉為encoding編碼,並且,它儘可能的將指定地編碼直接寫成Unicode文本。 注意,coding:encoding只是告訴Python檔案使用了encoding格式的編碼,但是編輯器可能會以自己的方式儲存.py檔案,因此最後檔案儲存的時候還需要編碼中選指定的ecoding才行。二、中文字元的儲存 >>> str = u"中文" >>> str u'\xd6\xd0\xce\xc4' >>> str = "中文" >>> str '\xd6\xd0\xce\xc4' u"中文"只是聲明unicode,實際的編碼並沒有變。這樣子就發生變化了: >>> str = "中文" >>> str '\xd6\xd0\xce\xc4' >>> str = str.decode("gb2312") >>> str u'\u4e2d\u6587'更進一步: >>> s = '中文' >>> s.decode('gb2312') u'\u4e2d\u6587' >>> len(s) 4 >>> len(s.decode('gb2312')) 2 >>> s = u'中文' >>> len(s) 4 >>> s = '中文test' >>> len(s) 8 >>> len(s.decode('gb2312')) 6 >>> s = '中文test,' >>> len(s) 10 >>> len(s.decode('gb2312')) 7 可以看出,對於實際Non-ASCII編碼儲存的字串,python可以正確的識別出其中的中文字元以及中文上下文中的標點符號。 首碼“u”表示“後面這個字串“是一個Unicode字串”,這僅僅是一個聲明,並不表示這個字串就真的是Unicode了;就好比某正太聲稱自己已滿18歲,但實際上他的真實年齡並不確定,現在體育界年齡造假可不稀罕么! 那麼聲明成u有什麼作用呢?對於Python來說,只要你聲明某字串是Unicode,它就會用Unicode的一套機制對它進行處理。比方說,做字串操作的時候會動用到內部的Unicode處理函數,儲存的時候以Unicode字元(雙位元組)進行儲存。等等。顯而易見,對於一個實際上並不是Unicode的字串,做Unicode動作的處理,是有可能會出問題的。u首碼只適用於你的字串常量真的是Unicode的情況。三、中文字元的IO操作 用python處理字串很容易,但是在處理中文的時候需要注意一些問題。比如: a = "我們是python愛好者" print a[0]只能輸出“我”字的前半部分,要想輸出整個的“我”字還需要: b = a[0:2] print b才行,很不方便,並且當一段文本中同時有中英文如何處理?最好的辦法就是轉換為unicode。像這樣: c = unicode(a, "gb2312") print c[0]這個時候c的下標對應的就是每一個字元,不再是位元組,並且通過len(c)就可以獲得字元數!還可以很方便的轉換為其他編碼,比如轉換為utf-8: d = c.encode("utf-8")四、<type ‘str’>和<type ‘unicode’><type ‘str’>將字串看作是位元組的序列,而<type ‘unicode’>則將其看作是字元的序列,單個字元可能佔用多個位元組;位元組相對於字元,其在儲存層次中更低一些。str轉換為unicode要decode,可以這樣想,因為要把位元組序列解釋成字元序列,位元組序列是底層的存放方式,解碼(decode)成更高層的字元以便使用;同理,unicode轉換為str要encode,就象資訊編碼(encode)後才儲存一樣: s.decode(encoding) <type 'str'> to <type 'unicode'> u.encode(encoding) <type 'unicode'> to <type 'str'> 例如: >>> s = 'str' >>> type(s) <type 'str'> >>> type(s.decode()) <type 'unicode'> >>> s = u'str' >>> type(s) <type 'unicode'> >>> type(s.encode()) <type 'str'>處理中文資料時最好採用如下方式: 1. Decode early(儘早decode, 將檔案中的內容轉化成unicode再進行下一步處理) 2. Unicode everywhere (程式內部處理都用unicode) 3. Encode late (最後encode回所需的encoding, 例如把最終結果寫進結果檔案)下面是一個簡單的示範,用re庫查詢一個中文字串並列印: >>> p = re.compile(unicode("測試(.*)", "gb2312")) >>> s = unicode("測試一二三", "gb2312") >>> for i in p.findall(s): print i.encode("gb2312") 一二三五、跨平台處理技巧 如果一個project必須在兩個平台上開發,程式應該使用同樣的encoding,比如要求所有的檔案都使用UTF-8,如果實在不能統一(一般是為了滿足許多所謂專家學者莫名其妙的要求),可以退而求其次,用當前系統編碼決定檔案內的編碼: import locale import string import re #根據當前系統的encoding構造需要的編碼取值 lang = string.upper(locale.setlocale(locale.LC_ALL, "")) textencoding = None #檢查編碼的值是不是滿足我們需要的情況 if re.match("UTF-8", lang) != None: # UTF-8編碼 textencoding = "utf-8" elif re.match(r"CHINESE|CP936", lang): # Windows下的GB編碼 textencoding = "gb18030" elif re.match(r"GB2312|GBK|GB18030", lang): # Linux下的GB編碼 textencoding = "gb18030" else: # 其他情況,拋個錯誤吧 raise UnicodeError fd = file(filename, "r") fulltextlist = fd.readlines() # 把每一行轉換成unicode for each in len(fulltextlist): fulltextlist[i] = unicode(each, textencoding) fd.close() # 如果要列印的話,可以用text.encode(encoding)來恢複成多位元組編碼小結 一個比較一般的Python中文處理的流程: * 將欲處理的字串用unicode函數以正確的編碼轉換為Unicode * 在程式中統一用Unicode字串進行操作 * 輸出時,使用encode方法,將Unicode再轉換為所需的編碼 有幾點要說明一下: * 所謂“正確的”編碼,指得是指定編碼和字串本身的編碼必須一致。這個其實並不那麼容易判斷,一般來說,我們直接輸入的簡體中文字元,有兩種可能的編碼:GB2312(GBK、GB18030)、以及UTF-8 * encode成本地編碼的時候,必須要保證目標編碼中存在欲轉換字元的內碼。encode這種操作一般是通過一個本地編碼對應Unicode的編碼轉換表來進行的,事實上每個本地編碼只能映射到Unicode的一部分。但是映射的地區是不同的,比如Big-5對應的Unicode的編碼範圍和 GBK對應的就不一樣(實際上這兩個編碼有部分範圍是重疊的)。所以,Unicode的一些字元(比如本身就是從GB2312轉換來的那些),可以映射到 GBK,但未必可以映射到Big-5,如果你想轉換到Big-5,很有可能就會出現編碼找不到的異常。但UTF-8的碼錶範圍實際上和Unicode是一樣的(只是編碼形式不同而已),所以,理論上來說,任何本地編碼的字元,都可以被轉換到UTF-8 * GB2312、GBK、GB18030本質上是同一種編碼通訊協定。只是在前者的基礎上擴充了字元數量 * UTF-8和GB編碼不相容
2.Python模組之codesc:自然語言編碼轉換
轉自:http://blog.csdn.net/zhaoweikid/archive/2007/06/07/1642015.aspx
python對多國語言的處理是支援的很好的,它可以處理現在任意編碼的字元,這裡深入的研究一下python對多種不同語言的處理。有一點需要清楚的是,當python要做編碼轉換的時候,會藉助於內部的編碼,轉換過程是這樣的:原有編碼 -> 內部編碼 -> 目的編碼python的內部是使用unicode來處理的,但是unicode的使用需要考慮的是它的編碼格式有兩種,一是UCS-2,它一共有65536個碼位,另一種是UCS-4,它有2147483648g個碼位。對於這兩種格式,python都是支援的,這個是在編譯時間通過--enable-unicode=ucs2或--enable-unicode=ucs4來指定的。那麼我們自己預設安裝的python有的什麼編碼怎麼來確定呢?有一個辦法,就是通過sys.maxunicode的值來判斷:import sysprint sys.maxunicode如果輸出的值為65535,那麼就是UCS-2,如果輸出是1114111就是UCS-4編碼。我們要認識到一點:當一個字串轉換為內部編碼後,它就不是str類型了!它是unicode類型:a = "風捲殘雲"print type(a)b = a.unicode(a, "gb2312")print type(b)輸出:<type 'str'><type 'unicode'>這個時候b可以方便的任意轉換為其他編碼,比如轉換為utf-8:c = b.encode("utf-8")print cc輸出的東西看起來是亂碼,那就對了,因為是utf-8的字串。好了,該說說codecs模組了,它和我上面說的概念是密切相關的。codecs專門用作編碼轉換,當然,其實通過它的介面是可以擴充到其他關於代碼方面的轉換的,這個東西這裡不涉及。#-*- encoding: gb2312 -*-import codecs, sysprint '-'*60# 建立gb2312編碼器look = codecs.lookup("gb2312")# 建立utf-8編碼器look2 = codecs.lookup("utf-8")a = "我愛北京天安門"print len(a), a# 把a編碼為內部的unicode, 但為什麼方法名為decode呢,我的理解是把gb2312的字串解碼為unicodeb = look.decode(a)# 返回的b[0]是資料,b[1]是長度,這個時候的類型是unicode了print b[1], b[0], type(b[0])# 把內部編碼的unicode轉換為gb2312編碼的字串,encode方法會返回一個字串類型b2 = look.encode(b[0])# 發現不一樣的地方了吧?轉換回來之後,字串長度由14變為了7! 現在的返回的長度才是真正的字數,原來的是位元組數print b2[1], b2[0], type(b2[0])# 雖然上面返回了字數,但並不意味著用len求b2[0]的長度就是7了,仍然還是14,僅僅是codecs.encode會統計字數print len(b2[0])上面的代碼就是codecs的使用,是最常見的用法。另外還有一個問題就是,如果我們處理的檔案裡的字元編碼是其他類型的呢?這個讀取進行做處理也需要特殊的處理的。codecs也提供了方法.#-*- encoding: gb2312 -*-import codecs, sys# 用codecs提供的open方法來指定開啟的檔案的語言編碼,它會在讀取的時候自動轉換為內部unicodebfile = codecs.open("dddd.txt", 'r', "big5")#bfile = open("dddd.txt", 'r')ss = bfile.read()bfile.close()# 輸出,這個時候看到的就是轉換後的結果。如果使用語言內建的open函數來開啟檔案,這裡看到的必定是亂碼print ss, type(ss)上面這個處理big5的,可以去找段big5編碼的檔案試試。
3.Python中文問題研究
轉自:http://blog.chinaunix.net/u2/60332/showart_2109290.html
我曾經在深入淺出java中文問題系列中研究過java 的中文問題,現在中文問題已經不再羈絆我在java世界中漫遊的腳步了。最近,對Python產生了濃厚的興趣,誰知道跟中文問題這個老朋友又一次不期而遇。看來,在代碼世界中,中文問題會在很長一段時間裡跟我們形影不離。這也難怪,誰讓當初發明電腦的不是我們中國人呢,否則,現在全世界的電腦都支援而且必須支援GBK,這樣,寫這樣文章的人就不會是我了,而是大洋彼岸的一個金髮碧眼的程式員,而且標題也相應改為 “studying the english problem in '大蟒' ”。。哈哈 YY而已,還是面對現實問題吧。相對java而言,中文問題在Python中的表現更為激烈。“激烈”的意思不是說更為嚴重或者說難於解決,只是 Python對於decode&encode錯誤的預設處理方式為strict,也就是直接報錯,而java使用replace的方式來處理了,因此 java出現中文問題後會列印出很多"??"。此外,Python的預設的encoding是ASCII,而java的預設encoding跟作業系統的 encoding是一致的。在這一點上,我覺得java更為合理,這樣對程式員更為友好,也減少了newbies 開始時的挫折感,是有利於語言的推廣的。但是,Python也有它的道理,畢竟ASCII是唯一的全世界所有平台都支援的字元集,而且問題始終是問題,始終會出現的,逃避它還不如早點面對它。 好了,說了這麼多,該說說Python中中文問題的癥狀了。在這之前,我們先要瞭解Python中有兩種字串,分別是一般的字串(每個字元用8 bits表示)和Unicode字串(每個字元用一個或者多個位元組表示),它們可以相互轉換。關於Unicode,Joel Spolsky 在 The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) 中有生動的說明,Jason Orendorff 在 Unicode for programmers 有著更為全面的描述,在此我就不再多說什麼了。來看下面的代碼:x = u"中文你好"print s 運行上述代碼,Python會給出下面的錯誤提示SyntaxError: Non-ASCII character '\xd6' in file G:\workspace\chinese_problem\src\test.py on line 1, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details 說是遇到非ASCII字元了,並讓我們參考pep-0263。PEP-0263(Python Enhancement Proposal)上面說得很清楚了,Python也意識到了國際化問題,並提出瞭解決方案。根據提案上面的要求,我們有如下代碼 # -*- coding:gb2312 -*- #必須在第一行或者第二行print "-------------code 1----------------"a = "中文a我愛你"print aprint a.find("我")b = a.replace("愛", "喜歡")print bprint "--------------code 2----------------"x = "中文a我愛你"y = unicode(x, "gb2312")print y.encode("gb2312")print y.find(u"我")z = y.replace(u"愛", u"喜歡")print z.encode("gb2312")print "---------------code 3----------------"print y 程式啟動並執行結果如下:-------------code 1----------------中文a我愛你5中文a我喜歡你--------------code 2----------------中文a我愛你3中文a我喜歡你---------------code 3----------------Traceback (most recent call last): File "G:\Downloads\eclipse\workspace\p\src\hello.py", line 16, in <module> print yUnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128) 我們可以看到,通過引入編碼聲明,我們可以正常地在使用中文了,而且在code 1和2中,控制台也能正確的把中文列印出來。但是,很明顯,上面的代碼也反映出了不少的問題: 1、code 1 和 2在使用print時採用了不同的方式,1是直接print,而2在print之前先進行編碼 2、code 1 和 2中在同樣的字串尋找同一個字元“我”,得出的結果不一樣(分別是5和3) 3、code 3 中直接列印unicode字串 y時出現錯誤(這也是為什麼code 2中要先進行編碼的原因) 為什嗎?為什嗎?我們可以先在腦海中類比一下我們使用Python的流程:首先,我們先用編輯器編寫好原始碼,儲存成檔案。如果原始碼中有編碼聲明而且用的編輯器支援該文法,那麼該檔案就以相應的編碼方式儲存在磁碟中。注意:編碼聲明和源檔案的編碼不一定是一致的,你完全可以在編碼聲明中聲明編碼為UTF-8,但是用GB2312來儲存源檔案。當然,我們不可能自尋煩惱,故意寫錯,而且好的IDE也能強制保證兩者的一致性,但是,如果我們用記事本或者EditPlus等編輯器來編寫代碼的話,一不小心就會出現這種問題的。 得到一個.py檔案後,我們就可以運行它了,這是,我們就把代碼交給Python解析器來完成解析工作。解析器讀入檔案時,先解析檔案中的編碼聲明,我們假設檔案的編碼為gb2312,那麼先將檔案中的內容由gb2312轉換成Unicode,然後再把這些Unicode轉換為UTF-8格式的位元組串。完成這一步驟後,解析器把這些UTF-8位元組串分段,解析。如果遇到使用Unicode字串,那麼就使用相應的UTF-8位元組串建立Unicode字串,如果程式中使用的是一般的字串,那麼解析器先將UTF-8位元組串通過Unicode轉換成相應編碼(這裡就是gb2312編碼)的位元組串,並用其建立一般的字串對象。也就是說,Unicode字串跟一般字串在記憶體中的存放格式是不一樣的,前者使用UTF-8的格式,後者使用GB2312格式。 好了,記憶體中的字串存放格式我們知道了,下面我們要瞭解print的工作方式。print其實只是負責把記憶體中相應的位元組串交給作業系統,讓作業系統相應的程式(譬如cmd視窗)進行顯示。這裡有兩種情況: 1、若字串是一般的字串,那麼print只需把記憶體中相應的位元組串推送給作業系統。如例子中的code 1。 2、如果字串是Unicode字串,那麼print在推送之前先進行相應的encode:我們可以顯示使用Unicode的encode方法使用合適的編碼方式來編碼(例子中code 2),否則Python使用預設的編碼方式進行編碼,也就是ASCII(例子中的code 3)。當然ASCII是不可能正確編碼中文的,因此Python報錯。 至此,上面的三個問題我們已經可以解析第一和第三個了。至於第二個問題,因為Python中有兩種字串,一般字串和Unicode字串,兩者都有各自的字元處理方法。對於前者,方法是以位元組的方式進行的,而且在GB2312中,每個漢字佔用兩個位元組,因此得到的結果是5;對於後者,也就是 Unicode字串,所有字元都是統一看待的,因此得到3。 雖然上面只提到了控制台程式的中文問題,但是檔案讀寫以及網路傳輸中出現的中文問題在原理上都是類似的。Unicode的出現可以很大程度上解決軟體的國際化問題,同時Python為Unicode提供了極為良好的支援,因此,我建議大家在編寫Python的程式時,都統一使用Unicode方式。儲存檔案時使用UTF-8的編碼方式。How to Use UTF-8 with Python有詳細的描述,大家可以參考一下。 Python中能導致出現中文問題的地方還很多,譬如檔案的讀寫,網路資料的傳輸等,希望大家能多多交流,共同解決這些問題。