python黑魔法之编码转换

作者:icedoom 时间:2022-08-11 10:03:41 

我们在使用其他语言的库做编码转换时,对于无法理解的字符,通常的处理也只有两种(或三种):

  • 抛异常

  • 替换成替代字符

  • 跳过

但是在复杂的现实世界中,由于各种不靠谱,我们处理的文本总会出现那么些不和谐因素,比如混合编码。在这种情况下,又回到了上面的处理办法。

那么问题来了,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,'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)

上面这个是我从网上copy的。开始我觉得很不错,但是后来发现是个很不经推敲的算法。
比如utf8和gbk在前两个字节就有交集的部分。当一个utf8的字符串以gbk编码decode的时候,出现错误是从第三个字节开始(前两个字节也能够在gbk编码范围中对应到一个汉字)。
如:


a = "你"              # utf8编码:'\xe4\xbd\xa0'
c = unicode(a[:2],'gbk')  # 正常返回
c = unicode(a, 'gbk')    # 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 ('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)

当然,这个逻辑其实还是不够严谨的。虽然对于这种混合编码这种畸形活处理有点较真儿。
不过既然python提供这样的能力,大家可以一起来讨论下,我们怎么可以做的更好?

标签:python,编码转换
0
投稿

猜你喜欢

  • asp error对象基础

    2008-08-04 13:25:00
  • IE8 的 noscript 标签 Bug

    2009-08-18 12:37:00
  • python单元测试框架pytest介绍

    2021-06-11 15:28:55
  • ASP 获取文件扩展名函数getFileExt()

    2011-03-11 11:18:00
  • 利用sys.sysprocesses检查SqlServer的阻塞和死锁

    2011-11-03 17:24:11
  • javascript常用的方法整理

    2023-08-23 10:57:13
  • 理解Python数据离散化手写if-elif语句与pandas中cut()方法实现

    2023-02-24 10:33:33
  • PHP Document 代码注释规范

    2023-11-14 11:50:54
  • IE bug: 1像素的dotted/dashed边框

    2009-10-28 18:45:00
  • 10个最容易犯的HTML标签错误

    2010-09-13 12:37:00
  • 教你用压缩技术给SQL Server备份文件瘦身

    2009-03-05 14:59:00
  • FrontPage XP设计制作网页小技巧八则

    2008-06-04 12:43:00
  • python操作toml文件的示例代码

    2023-06-12 03:58:33
  • asp里Sub与Function有什么区别

    2007-08-17 10:39:00
  • 浅谈Python批处理文件夹中的txt文件

    2023-03-17 22:58:28
  • 去除DW MX 2004表格宽度辅助

    2010-09-02 12:37:00
  • 解析isset与is_null的区别

    2023-11-21 16:11:44
  • 使用MyISAM表和InnoDB的一些记录

    2009-12-20 18:21:00
  • canvas实现手机端用来上传用户头像的代码

    2023-09-16 02:30:54
  • SQL Server数据库入门学习总结

    2012-08-21 11:01:33
  • asp之家 网络编程 m.aspxhome.com