Python 3.x中處理文字的對象有str, bytes, bytearray。
bytes有一個類方法,可以通過序列來構建字符串,而這個方法不可以用在str上。
>>> b = bytes.fromhex('E4 B8 AD')>>> bb'/xe4/xb8/xad'>>> b.decode('utf-8')'中'>>> str(b)"b'//xe4//xb8//xad'"采用chr可以把一個Unicode的code point轉換為字符,通過ord可以進行反向操作。
>>> ord('A')65>>> ord('中')20013>>> chr(65)'A'>>> chr(20013)'中'>>> len('中')1>>> '中'.encode('utf-8')b'/xe4/xb8/xad'>>> len('中'.encode('utf-8')) #計算的是bytes對象的長度,包含3個整數字符3在Python接受我們的輸入時,總是會先轉為Unicode。而且這個過程越早越好。
然后Python的處理總是對Unicode進行的,在這個過程中,一定不要進行編碼轉換的工作。
在Python向我們返回結果時,總是會從Unicode轉為我們需要的編碼。而且這個過程越晚越好。
Python默認使用utf-8編碼。
如果想使用一種不同的編碼方式來保存Python代碼,我們可以在每個文件的第一行或者第二行(如果第一行被hash-bang命令占用了)放置編碼聲明(encoding declaration)# ‐*‐ coding: windows‐1252 ‐*‐
C:/Users/JL>chcp #查找操作系統使用的編碼Active code page: 936>>> import sys, locale>>> locale.getpreferredencoding() #這個是最重要的'cp936'>>> my_file = open('cafe.txt','r')>>> type(my_file)<class '_io.TextIOWrapper'>>>> my_file.encoding #文件對象默認使用locale.getpreferreddecoding()的值'cp936'>>> sys.stdout.isatty(), sys.stdin.isatty(), sys.stderr.isatty() #output是否是控制臺console(True, True, True)>>> sys.stdout.encoding, sys.stdin.encoding, sys.stderr.encoding #sys的標準控制流如果被重定向,或者定向到文件,那么編碼將使用環境變量PYTHONIOENCODING的值、控制臺console的編碼、或者locale.getpreferredencoding()的編碼,優先級依次減弱。('cp936', 'cp936', 'cp936')>>> sys.getdefaultencoding() #如果Python需要把二進制數據轉為字符對象,那么在缺省情況下使用該值。'utf-8'>>> sys.getfilesystemencoding() #Python用來編碼或者解碼文件名(不是文件內容)的時候,默認使用該編碼。'mbcs'以上是在Windows中的測試結果,如果在GNU/linux或者OSX中,那么所有的結果都是UTF-8.
關于mbcs和utf-8的區別,可以參考http://stackoverflow.com/questions/3298569/difference-between-mbcs-and-utf-8-on-windows
>>> pen('cafe.txt','w',encoding='utf-8').write('café')4>>> fp = open('cafe.txt','r')>>> fp.read()'caf茅'>>> fp.encoding'cp936'>>> open('cafe.txt','r', encoding = 'cp936').read()'caf茅'>>> open('cafe.txt','r', encoding = 'latin1').read()'caf??'>>> fp = open('cafe.txt','r', encoding = 'utf-8')>>> fp.encoding'utf-8'從上面的例子可以看出,無論什么時候都不要使用默認的編碼,因為在不同的機器上運行的時候會出現意想不到的問題。
Python總是通過code point來比較字符串的大小,或者是否相等的。
Unicode中重音符號有兩種表示方法,用一個字節表示,或者用基字母加上重音符號表示,在Unicode中他們是相等的,但是在Python中由于通過code point來比較大小,所以就不相等了。
>>> c1 = 'cafe/u0301'>>> c2 = 'café'>>> c1 == c2False>>> len(c1), len(c2)(5, 4)解決方法是通過unicodedata庫中的normalize函數,該函數的第一個參數可以接受"NFC",'NFD','NFKC','NFKD'四個參數中的一個。
NFC(Normalization Form Canonical Composition):以標準等價方式來分解,然后以標準等價重組之。若是singleton的話,重組結果有可能和分解前不同。盡可能的縮短整個字符串的長度,所以會把'e/u0301'2個字節壓縮到一個字節'é'。
NFD(Normalization Form Canonical Decomposition):以標準等價方式來分解
NFKD(Normalization Form Compatibility Decomposition):以相容等價方式來分解
NFKC(Normalization Form Compatibility Composition):以相容等價方式來分解,然后以標準等價重組之。
NFKC和NFKD可能會引起數據損失。
from unicodedata import normalize>>> c3 = normalize('NFC',c1) #把c1往字符串長度縮短的方向操作>>> len(c3)4>>> c3 == c2True>>> c4 = normalize('NFD',c2)>>> len(c4)5>>> c4 == c1True西方的鍵盤通常會鍵入盡可能短的字符串,也就是說和"NFC"的結果一致,但是通過"NFC"來操作一下再比較字符串是否相等比較安全。且W3C建議使用"NFC"的結果。
同樣的一個字符在Unicode中有兩個不同的編碼。
該函數會把一個單一的Unicode字符轉為另一個Unicode字符。
>>> o1 = '/u2126'>>> o2 = '/u03a9'>>> o1, o2('?', 'Ω')>>> o1 == o2False>>> name(o1), name(o2)('OHM SIGN', 'GREEK CAPITAL LETTER OMEGA')>>> o3 = normalize('NFC',o1)>>> name(o3)'GREEK CAPITAL LETTER OMEGA'>>> o3 == o2True又比如
>>> u1 = '/u00b5'>>> u2 = '/u03bc'>>> u1,u2('μ', 'μ')>>> name(u1), name(u2)('MICRO SIGN', 'GREEK SMALL LETTER MU')>>> u3 = normalize('NFKD',u1)>>> name(u3)'GREEK SMALL LETTER MU'再一個例子
>>> h1 = '/u00bd'>>> h2 = normalize('NFKC',h1)>>> h1, h2('?', '1?2')>>> len(h1), len(h2)(1, 3)有時候我們希望使用不區分大小寫的形式進行比較
使用方法str.casefold(),該方法會把大寫字母轉換為小寫進行比較,比如'A'會轉為'a','MICRO SIGN'的'μ'會轉換為'GREEK SMALL LETTER MU'的'μ'
在絕大部分(98.9%)情況下str.casefold()和str.lower()的結果一致。
文字排序
由于不同的語言規則,如果單純按照Python的比較code point的方式進行,那么會出現很多不是用戶期望的結果。
通常采用locale.strxfrm進行排序。
>>> import locale>>> locale.setlocale(locale.LC_COLLATE,'pt_BR.UTF-8')'pt_BR.UTF-8'>>> sort_result = sorted(intial, key = locale.strxfrm)如果是Python源碼中出現了解碼錯誤,那么會產生SyntaxError異常。
其他情況下,如果發現編碼解碼錯誤,那么會產生UnicodeEncodeError, UnicodeDecodeError異常。
from unicodedata import normalize, combiningdef nfc_equal(s1, s2): '''return True if string s1 is eual to string s2 after normalization under "NFC" ''' return normalize("NFC",s1) == normalize("NFC",s2)def fold_equal(s1, s2): '''return True if string s1 is eual to string s2 after normalization under "NFC" and casefold()''' return normalize('NFC',s1).casefold() == normalize('NFC',s2).casefold()def shave_marks(txt): '''Remove all diacritic marks basically it only need to change Latin text to pure ASCII, but this func will change Greek letters also below shave_latin_marks func is more precise''' normal_txt = normalize('NFD',txt) shaved = ''.join(c for c in normal_txt if not combining(c)) return normalize('NFC',shaved)def shave_latin_marks(txt): '''Remove all diacritic marks from Latin base characters''' normal_txt = normalize('NFD',txt) keeping = [] latin_base=False for c in normal_txt: if combining(c) and latin_base: continue #Ingore diacritic marks on Latin base char keeping.append(c) #If it's not combining char, it should be a new base char if not combining(c): latin_base = c in string.ascii_letters這是Python的標準模塊。
參考資料:
新聞熱點
疑難解答