python黑魔法之編碼轉(zhuǎn)換
我們在使用其他語言的庫做編碼轉(zhuǎn)換時(shí),對于無法理解的字符,通常的處理也只有兩種(或三種):
- 拋異常
- 替換成替代字符
- 跳過
但是在復(fù)雜的現(xiàn)實(shí)世界中,由于各種不靠譜,我們處理的文本總會(huì)出現(xiàn)那么些不和諧因素,比如混合編碼。在這種情況下,又回到了上面的處理辦法。
那么問題來了,python有沒有更好地辦法呢?
答案是,有!
python的編碼轉(zhuǎn)換流程實(shí)際上是兩段式轉(zhuǎn)換:
source -> unicode -> dest
首先將字符串從原始編碼轉(zhuǎn)換成unicode。再將unicode轉(zhuǎn)換成目標(biāo)編碼。
第一步我們一般采用decode()或者 unicode() 這兩個(gè)函數(shù)完成。
第二步我們使用encode()函數(shù)完成。
在這里我們說的黑魔法就是在第一步實(shí)現(xiàn)。
decode和unicode函數(shù)都有一個(gè)叫做errors的可選參數(shù)。看看官方的描述:
- errors may be given to set a different error
- handling scheme. Default is 'strict' meaning that encoding errors raise
- a UnicodeDecodeError. Other possible values are 'ignore' and 'replace'
- as well as any other name registered with codecs. register_error that is
- able to handle UnicodeDecodeErrors.
這個(gè)參數(shù)通常有三種值:
- strict 默認(rèn)值。如果出現(xiàn)編碼錯(cuò)誤,則會(huì)拋出UnicodeDecodeError。
- ignore 跳過。
- replace 用?替換。
好了,看到最后一句話了嗎?好戲上演了!
模塊codec有一個(gè)函數(shù)叫做register_error。他的作用讓用戶可以注冊自定義的errors處理方法。
用來處理UnicodeDecodeError。
我們看看函數(shù)原型:
codecs.register_error(name, error_handler)
name: 錯(cuò)誤處理的名稱。用以填寫在decode函數(shù)的error參數(shù)中。
error_handler: 處理函數(shù)。該函數(shù)接受一個(gè)異常參數(shù)。
返回一個(gè)tuple,該tuple有2個(gè)元素,第一個(gè)是糾錯(cuò)后的字符串,第二個(gè)是繼續(xù)decode的起始位置
有了上面的基本概念。我們看下具體實(shí)現(xiàn):
def cjk_error(e): if not isinstance(e, UnicodeDecodeError): raise TypeError("don't know how to handle %r" % exc) if exc.end + 1 > len(exc.object): raise TypeError('unknown codec ,the object too short!') ch1 = ord(exc.object[exc.start:exc.end]) newpos = exc.end + 1 ch2 = ord(exc.object[exc.start + 1:newpos]) sk = exc.object[exc.start:newpos] if 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0x7E<=ch2<=0xFE): # GBK return (unicode(sk,'cp936'), newpos) if 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0xA1<=ch2<=0xFE): # BIG5 return (unicode(sk,'big5'), newpos) raise TypeError('unknown codec !') codecs.register_error("cjk_replace", cjk_replace)
上面這個(gè)是我從網(wǎng)上copy的。開始我覺得很不錯(cuò),但是后來發(fā)現(xiàn)是個(gè)很不經(jīng)推敲的算法。
比如utf8和gbk在前兩個(gè)字節(jié)就有交集的部分。當(dāng)一個(gè)utf8的字符串以gbk編碼decode的時(shí)候,出現(xiàn)錯(cuò)誤是從第三個(gè)字節(jié)開始(前兩個(gè)字節(jié)也能夠在gbk編碼范圍中對應(yīng)到一個(gè)漢字)。
如:
a = "你" # utf8編碼:'\xe4\xbd\xa0' c = unicode(a[:2],'gbk') # 正常返回 c = unicode(a, 'gbk') # UnicodeDecodeError 。錯(cuò)誤發(fā)生在第三個(gè)字節(jié)
所以針對這種情況,做了下改進(jìn):
import codec def cjk_replace(e): if not isinstance(e, UnicodeDecodeError): raise TypeError("invalid exception type %s" e) src = e.encoding if src in ('gbk','gb18030', 'big5'): beg = e.start - 2 if beg >= 0: try: return unicode(e.object[beg:e.end], 'utf8'), e.end + 1 except: pass if exc.end + 1 > len(exc.object): raise TypeError('unknown codec ,the object too short!') ch1 = ord(exc.object[exc.start:exc.end]) newpos = exc.end + 1 ch2 = ord(exc.object[exc.start + 1:newpos]) sk = exc.object[exc.start:newpos] if src != 'gbk' and 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0x7E<=ch2<=0xFE): # GBK return (unicode(sk,'cp936'), newpos) if src != 'big5' and 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0xA1<=ch2<=0xFE): # BIG5 return (unicode(sk,'big5'), newpos) raise TypeError('unknown codec !') codecs.register_error("cjk_replace", cjk_replace)
當(dāng)然,這個(gè)邏輯其實(shí)還是不夠嚴(yán)謹(jǐn)?shù)?。雖然對于這種混合編碼這種畸形活處理有點(diǎn)較真兒。
不過既然python提供這樣的能力,大家可以一起來討論下,我們怎么可以做的更好?
- Python黑魔法遠(yuǎn)程控制開機(jī)的實(shí)例
- Python黑魔法@property裝飾器的使用技巧解析
- Python黑魔法Descriptor描述符的實(shí)例解析
- python黑魔法之參數(shù)傳遞
- 詳解python metaclass(元類)
- python中metaclass原理與用法詳解
- Python探索之Metaclass初步了解
- 舉例講解Python中metaclass元類的創(chuàng)建與使用
- 詳解python單例模式與metaclass
- Python使用metaclass實(shí)現(xiàn)Singleton模式的方法
- Python中的Classes和Metaclasses詳解
- 深入理解Python中的元類(metaclass)
- Python黑魔法之metaclass詳情
相關(guān)文章
pyMySQL SQL語句傳參問題,單個(gè)參數(shù)或多個(gè)參數(shù)說明
這篇文章主要介紹了pyMySQL SQL語句傳參問題,單個(gè)參數(shù)或多個(gè)參數(shù)說明,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-065款最強(qiáng)且免費(fèi)的Python IDE小結(jié)
開發(fā)工具在日常代碼編寫過程中起著至關(guān)重要的作用,一款優(yōu)秀的開發(fā)工具,不僅可以盡可能的減少你在配置方面耗費(fèi)的精力,本文主要介紹了5種,感興趣的可以了解一下2021-07-07如何將python項(xiàng)目部署在一臺服務(wù)器上
服務(wù)器less技術(shù)是一種無需管理服務(wù)器即可運(yùn)行應(yīng)用程序的方法,最流行的服務(wù)器less平臺是AWS Lambda,這篇文章主要介紹了如何將python項(xiàng)目部署在一臺服務(wù)器上,需要的朋友可以參考下2023-10-10使用pyinstaller打包.exe文件的詳細(xì)教程
PyInstaller是一個(gè)跨平臺的Python應(yīng)用打包工具,能夠把 Python 腳本及其所在的 Python 解釋器打包成可執(zhí)行文件,下面這篇文章主要給大家介紹了關(guān)于使用pyinstaller打包.exe文件的相關(guān)資料,需要的朋友可以參考下2022-04-04python實(shí)現(xiàn)的簡單FTP上傳下載文件實(shí)例
這篇文章主要介紹了python實(shí)現(xiàn)的簡單FTP上傳下載文件的方法,實(shí)例分析了Python基于FTP模塊實(shí)現(xiàn)文件傳輸?shù)募记?需要的朋友可以參考下2015-06-06python?配置uwsgi?啟動(dòng)Django框架的詳細(xì)教程
這篇文章主要介紹了python?配置uwsgi?啟動(dòng)Django框架,本文給大家講解的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-12-12