首頁  >  文章  >  後端開發  >  python黑魔法之編碼轉換方法

python黑魔法之編碼轉換方法

高洛峰
高洛峰原創
2017-03-13 18:15:331116瀏覽

這篇文章主要介紹了python黑魔法之編碼轉換,分析了python編碼轉換的方法,感興趣的小伙伴們可以參考一下

我們在使用其他語言的函式庫做編碼轉換時,對於無法理解的字符,通常的處理也只有兩種(或三種):

  • 拋異常

  • #取代為替代字元

  • 跳過

但是在複雜的現實世界中,由於各種不靠譜,我們處理的文本總會出現那麼些不和諧因素,比如混合編碼。在這種情況下,又回到了上面的處理辦法。

那麼問題來了,python有沒有更好地辦法呢?

答案是,有!

python的編碼轉換流程其實是兩段式轉換:


#
source -> unicode -> dest

首先將字串從原始編碼轉換成unicode。再將unicode轉換成目標編碼。

第一步我們一般採用decode() unicode() 這兩個函數完成。
第二步驟我們使用encode()函數完成。

在這裡我們說的黑魔法就是在第一步實現。

decode和unicode函數都有一個叫做errors的可選參數。看看官方的描述:

  • #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.

#這個參數通常有三種值:

  • #strict 預設值。如果出現編碼錯誤,則會拋出UnicodeDecodeError。

  • ignore 跳過。

  • replace 用?來替換。

好了,看到最後一句話了嗎?好戲上演了!

模組codec有一個函數叫做register_error。他的作用讓使用者可以註冊自訂的errors處理方法。

用來處理UnicodeDecodeError。

我們來看看函數原型:


codecs.register_error(name, error_handler)

name:

錯誤處理的名稱。用以填寫在decode函數的error參數中。 error_handler: 處理函數。此函數接受一個異常參數。
回傳一個tuple,該tuple有2個元素,第一個是糾錯後的字串,第二個是繼續decode的起始位置

有了上面的基本概念。我們看下具體實作:


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,&#39;cp936&#39;), newpos) 
  if 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0xA1<=ch2<=0xFE): # BIG5 
    return (unicode(sk,&#39;big5&#39;), newpos) 
  raise TypeError(&#39;unknown codec !&#39;) 
codecs.register_error("cjk_replace", cjk_replace)

上面這個是我從網路上

copy的。開始我覺得很不錯,但是後來發現是個很不經推敲的演算法。 例如utf8和gbk在前兩個位元組就有交集的部分。當一個utf8的字串以gbk編碼decode的時候,出現錯誤是從第三個位元組開始(前兩個位元組也能夠在gbk編碼範圍中對應到一個漢字)。
如:


a = "你"              # utf8编码:&#39;\xe4\xbd\xa0&#39;
c = unicode(a[:2],&#39;gbk&#39;)  # 正常返回
c = unicode(a, &#39;gbk&#39;)    # UnicodeDecodeError 。错误发生在第三个字节

所以針對這種情況,做了下改進:


import codec

def cjk_replace(e):
  if not isinstance(e, UnicodeDecodeError):
    raise TypeError("invalid exception type %s" e)

  src = e.encoding
  if src in (&#39;gbk&#39;,&#39;gb18030&#39;, &#39;big5&#39;):
    beg = e.start - 2
    if beg >= 0:
      try:
        return unicode(e.object[beg:e.end], &#39;utf8&#39;), e.end + 1
      except:
        pass

  if exc.end + 1 > len(exc.object):
    raise TypeError(&#39;unknown codec ,the object too short!&#39;)
  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 != &#39;gbk&#39; and 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0x7E<=ch2<=0xFE): # GBK
    return (unicode(sk,&#39;cp936&#39;), newpos)
  if src != &#39;big5&#39; and 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0xA1<=ch2<=0xFE): # BIG5
    return (unicode(sk,&#39;big5&#39;), newpos)
  raise TypeError(&#39;unknown codec !&#39;)

codecs.register_error("cjk_replace", cjk_replace)

當然,這個邏輯其實還是不夠嚴謹的。雖然對於這種混合編碼這種畸形活處理有點較真兒。

不過既然python提供這樣的能力,大家可以一起來討論下,我們怎麼可以做的更好?

以上是python黑魔法之編碼轉換方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn