Python实现Unicode转中文及解决默认编码问题的方法

栗子
栗子
发布时间:2026-10-13 | 2人浏览过
原创

本文介绍Python中将Unicode编码转换为中文的方法,并深入分析Python2中修改默认编码为UTF-8的潜在风险。通过示例展示了改变sys.setdefaultencoding可能导致编码错误和字典行为异常,最后给出显式编码转换的最佳实践建议。

本文详细讲解Python中实现Unicode编码转换为中文的方法,以及处理默认编码设置的相关技巧,供读者参考学习。

在爬虫抓取网页数据时,经常需要将形如"\u4eba\u751f\u82e6\u77ed\uff0cpy\u662f\u5cb8"的Unicode编码字符串转换为可读的中文。这属于标准的Unicode中文编码格式,可通过以下两种方式进行转换:

方法一:

>>> s = u'\u4eba\u751f\u82e6\u77ed\uff0cpy\u662f\u5cb8'
>>> print s
人生苦短,py是岸

方法二:

>>> s = r'\u4eba\u751f\u82e6\u77ed\uff0cpy\u662f\u5cb8'
>>> s = s.decode('unicode_escape')
>>> print s
人生苦短,py是岸

此外,在Python2开发中,常会遭遇“UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)”这类编码报错。

通常可以通过以下代码片段来解决该问题:

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

上述操作将Python2的默认编码从ASCII修改为UTF-8。需要注意的是,这种方法并非一劳永逸,它可能导致部分现有代码的行为出现异常。

关于sys.setdefaultencoding('utf-8')的补充说明:

sys.setdefaultencoding('utf-8') 会引发两个主要问题

简而言之,这种做法会导致某些代码行为变得不可预测,且这些异常难以修复,往往以隐蔽Bug的形式存在。下面通过两个具体案例进行说明。

1. 编码解析错误

import chardet
def print_string(string):
  try:
    print(u"%s" % string)
  except UnicodeError:
    print u"%s" % unicode(byte_string, encoding=chardet.detect(string)['encoding'])
print_string(u"þ".encode("latin-1"))
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
print(key_in_dict('þ'))

运行结果如下:

$~ þ
$~ þ

在上述代码中,默认的ASCII编码无法直接解码该字符串。'þ'是Latin-1编码的十六进制表示(c3 be),显然超出了ASCII码集(仅128个字符)的范围,从而触发UnicodeError异常。异常处理机制会根据编码探测结果,尝试用可能的编码进行解码,因此能较为准确地输出'þ'。

然而,一旦将默认编码设置为UTF-8,由于UTF-8完全覆盖Latin-1,程序会直接使用UTF-8进行解码。在UTF-8编码中,字节序列c3 be对应字符'þ'。因此,最终打印出的字符与之前完全不同。

或许你会认为自己的代码不会这样写,或者会手动修正。但如果这是第三方库的代码呢?依赖的第三方库因此产生Bug。即使不依赖第三方库,下面这个关于字典的行为异常问题依然难以避免。

2. 字典操作行为不一致

假设我们需要检查字典中是否存在某个键,通常有两种实现方式。

#-*- coding: utf-8 -*-
d = {1:2, '1':'2', '你好': 'hello'}
def key_in_dict(key)
  if key in d:
    return True
  return False
def key_found_in_dict(key):
  for _key in d:
    if _key == key:
      return True
  return False

对比修改系统默认编码前后,这两种函数的输出差异。

#-*- coding: utf-8 -*-
print(key_in_dict('你好'))
print(key_found_dict('你好'))
print(key_in_dict(u'你好'))
print(key_found_in_dict(u'你好'))
print('------utf-8------')
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
print(key_in_dict('你好'))
print(key_found_dict('你好'))
print(key_in_dict(u'你好'))
print(key_found_in_dict(u'你好'))

输出结果如下:

$~ True
$~ True
$~ False
$~ False
$~ ------utf-8------
$~ True
$~ True
$~ False
$~ True

可以看出,在默认编码被修改后,两种函数的输出结果不再一致。

字典的'in'操作符会对键进行哈希计算,并通过比较哈希值来判断相等性。对于ASCII码集内的字符,无论是字节字符串类型还是Unicode类型,其哈希值是相同的,例如u'1' in {'1':1}返回True。但对于超出ASCII码集的字符(如本例中的'你好'),字节字符串类型的哈希值与Unicode类型的哈希值是不同的。

而'=='操作符会尝试进行一次类型转换,将字节字符串(byte string,如上面的'你好')转换为Unicode(u'你好')类型,然后比较转换后的结果。在默认的ASCII编码系统中,将'你好'转换为Unicode会产生Warning: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal,因为超出码集无法转换,系统默认判定两者不相等。当我们将系统编码手动改为UTF-8后,这一限制被解除,'你好'可以顺利转换为Unicode,最终导致'in'和'=='的行为出现不一致。

问题根源:Python2中的字符串类型

为了让语法看起来简洁易用,Python做了许多巧妙但复杂的设计,混淆字节字符串(byte string)和文本字符串(text string)便是其中之一。

在Python中,主要有三类字符串类型:unicode(文本字符串)、str(字节字符串,即二进制数据)以及basestring(前两者的父类)。

在语言设计领域,一串字节(sequences of bytes)是否应被视为字符串(string)一直存在争议。Java和C#持反对意见,而Python则站在支持者一边。实际上,许多针对文本的操作(如正则匹配、字符替换)对纯字节数据并无必要。Python认为字节即字符,因此两者的操作集合保持一致。

进而,Python会在必要时尝试对字节进行自动类型转换,例如在上述的'=='操作或字节与文本拼接时。如果没有指定编码(encoding),不同类型间的转换无法进行,因此Python需要一个默认编码。在Python2诞生的年代,ASCII是最流行的编码(可以说是最通用的),因此Python2选择了ASCII作为默认编码。然而,众所周知,在需要转换的场景中,ASCII往往无能为力(仅128个字符,适用范围极窄)。

经过多年的批评与反馈,Python 3终于做出了改进。其默认编码为Unicode,这意味着在所有需要转换的场合,都能正确且成功地进行转换。

最佳实践建议

讨论了这么多,如果暂时不迁移到Python 3,该如何应对呢?

这里有几条建议:

所有文本字符串(text string)都应该是Unicode类型,而不是str。如果你在操作文本却使用str类型,就是在制造Bug。

在需要转换时,进行显式转换。从字节解码为文本,使用var.decode(encoding);从文本编码为字节,使用var.encode(encoding)。

从外部读取数据时,默认将其视为字节,然后解码为所需的文本;同样,当需要向外部发送文本时,先编码为字节再发送。

PS:这里再为大家提供几款Unicode编码转换操作相关工具供大家参考使用:

在线Unicode/中文转换工具: http://tools.jb51.net/transcoding/unicode_chinese

Native/Unicode在线编码转换工具: http://tools.jb51.net/transcoding/native2unicode

在线中文汉字/ASCII码/Unicode编码互相转换工具: http://tools.jb51.net/transcoding/chinese2unicode

更多关于Python相关内容感兴趣的读者可查看本站专题:《Python编码操作技巧总结》、《Python图片操作技巧总结》、《Python数据结构与算法教程》、《Python Socket编程技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》、《Python入门与进阶经典教程》及《Python文件与目录操作技巧汇总》

希望本文所述对大家Python程序设计有所帮助。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表