python实现unicode转中文及转换默认编码的方法


Posted in Python onApril 29, 2017

本文实例讲述了python实现unicode转中文及转换默认编码的方法。分享给大家供大家参考,具体如下:

一、在爬虫抓取网页信息时常需要将类似"\u4eba\u751f\u82e6\u77ed\uff0cpy\u662f\u5cb8"转换为中文,实际上这是unicode的中文编码。可用以下方法转换:

1、

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

2、

>>> 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 编码 hex 表示是 c3 be ,显然是超出了只有128个字符的 ascii 码集的,引发 UnicodeError 异常,进入异常处理。异常处理则会根据编码探测,用最可能的编码来解码,会比较靠谱地输出 þ 。

而一旦我们将 defaultencoding 设置为 utf-8,因为 utf-8 的字符范围是完全覆盖 latin-1,因此,会直接使用 utf-8 进行解码。c3 be 在 utf-8 中,是 þ。于是我们打印出了完全不同的字符。

可能你们会说我们不会写这样的代码。如果我们写了也会做修正。但如果是第三方库这么写了呢?项目依赖的第三方库就这么 bug 了。如果你不依赖第三方库,那么下面这个 bug,还是逃不过。

2. dictionray 行为异常

假设我们要从一个 dictionary 里查找一个 key 是否存在,通常来说,有两种可行方法。

#-*- 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

可以看到,当默认编码改了之后,两个函数的输出不再一致。

dict 的 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 中的 string

Python 为了让其语法看上去简洁好用,做了很多 tricky 的事情,混淆 byte string 和 text string 就是其中一例。

在 Python 里,有三大类 string 类型,unicode(text string),str(byte string,二进制数据),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,如果你在操作 text,而类型却是 str,那就是在制造 bug。

在需要转换的时候,显式转换。从字节解码成文本,用 var.decode(encoding),从文本编码成字节,用 var.encode(encoding)。

从外部读取数据时,默认它是字节,然后 decode 成需要的文本;同样的,当需要向外部发送文本时,encode 成字节再发送。

Python 相关文章推荐
windows下安装python paramiko模块的代码
Feb 10 Python
Pyramid将models.py文件的内容分布到多个文件的方法
Nov 27 Python
python实现批量获取指定文件夹下的所有文件的厂商信息
Sep 28 Python
使用Python脚本将Bing的每日图片作为桌面的教程
May 04 Python
Python时间戳使用和相互转换详解
Dec 11 Python
python 寻找list中最大元素对应的索引方法
Jun 28 Python
python实现视频分帧效果
May 31 Python
Python完成毫秒级抢淘宝大单功能
Jun 06 Python
python批量修改图片尺寸,并保存指定路径的实现方法
Jul 04 Python
详解Python并发编程之创建多线程的几种方法
Aug 23 Python
python3中使用__slots__限定实例属性操作分析
Feb 14 Python
Python pandas读取CSV文件的注意事项(适合新手)
Jun 20 Python
Python 正则表达式实现计算器功能
Apr 29 #Python
python中类变量与成员变量的使用注意点总结
Apr 29 #Python
Python urls.py的三种配置写法实例详解
Apr 28 #Python
Python HTTP客户端自定义Cookie实现实例
Apr 28 #Python
Python 通过pip安装Django详细介绍
Apr 28 #Python
python 使用get_argument获取url query参数
Apr 28 #Python
Python、PyCharm安装及使用方法(Mac版)详解
Apr 28 #Python
You might like
php中理解print EOT分界符和echo EOT的用法区别小结
2010/02/21 PHP
PHP无限分类(树形类)
2013/09/28 PHP
PHP封装的一个支持HTML、JS、PHP重定向的多功能跳转函数
2014/06/19 PHP
javascript中创建对象的三种常用方法
2010/12/30 Javascript
jQuery简单实现中间浮窗效果
2016/09/04 Javascript
easyui关于validatebox实现多重规则验证的方法(必看)
2017/04/12 Javascript
Javascript ES6中对象类型Sets的介绍与使用详解
2017/07/17 Javascript
ReactJs实现树形结构的数据显示的组件的示例
2017/08/18 Javascript
使用Node.js实现ORM的一种思路详解(图文)
2017/10/24 Javascript
Angular路由ui-router配置详解
2018/08/01 Javascript
记录一次开发微信网页分享的步骤
2019/05/07 Javascript
压缩Vue.js打包后的体积方法总结(Vue.js打包后体积过大问题)
2020/02/03 Javascript
vue中实现动态生成二维码的方法
2020/02/21 Javascript
vue elementui tree 任意级别拖拽功能代码
2020/08/31 Javascript
[01:02:26]DOTA2-DPC中国联赛 正赛 SAG vs RNG BO3 第二场 1月18日
2021/03/11 DOTA
Python的装饰器用法学习笔记
2016/06/24 Python
开源软件包和环境管理系统Anaconda的安装使用
2017/09/04 Python
Python中最大最小赋值小技巧(分享)
2017/12/23 Python
使用python Fabric动态修改远程机器hosts的方法
2018/10/26 Python
在Mac下使用python实现简单的目录树展示方法
2018/11/01 Python
学Python 3的理由和必要性
2019/11/19 Python
python pygame实现球球大作战
2019/11/25 Python
python实现录音功能(可随时停止录音)
2020/10/26 Python
python自动从arxiv下载paper的示例代码
2020/12/05 Python
CSS3使用border-radius属性制作圆角
2014/12/22 HTML / CSS
求职简历的自我评价怎样写好
2013/10/07 职场文书
大学四年个人自我小结
2014/03/05 职场文书
秋季开学典礼主持词
2014/03/19 职场文书
推荐信格式范文
2014/05/09 职场文书
学校食品安全实施方案
2014/06/14 职场文书
2014副镇长民主生活会个人对照检查材料思想汇报
2014/09/30 职场文书
保研专家推荐信范文
2015/03/25 职场文书
党支部评议意见
2015/06/02 职场文书
个人欠条范本
2015/07/03 职场文书
2016高三毕业赠言寄语
2015/12/04 职场文书
聊聊mysql都有哪几种分区方式
2022/04/13 MySQL