前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >由__future__中unicode_literals引起的错误来研究python中的编码问题

由__future__中unicode_literals引起的错误来研究python中的编码问题

作者头像
the5fire
发布2019-02-28 17:36:17
1.2K0
发布2019-02-28 17:36:17
举报

在py2.7的项目中用了future模块中的 unicode_literals 来为兼容py3.x做准备,今天遇到一个UnicodeEncodeError的错误,跟了下,发现这个小坑值得注意。是怎么样的一个坑呢?跟着代码看看。顺便深究一下原理。

1. 未引入unicode_literals版本

.. code:: python

代码语言:javascript
复制
#coding:utf-8
from datetime import datetime

now = datetime.now()
print now.strftime('%m月%d日 %H:%M')

这段代码可以正常执行输出: 03月12日 21:53

2. 引入unicode_literals

.. code:: python

代码语言:javascript
复制
#coding:utf-8
from __future__ import unicode_literals
from datetime import datetime

now = datetime.now()
print now.strftime('%m月%d日 %H:%M')

抛出如下错误::

代码语言:javascript
复制
Traceback (most recent call last):
File "unicode_error_demo2.py", line 7, in <module>
      print now.strftime('%m月%d日 %H:%M')
UnicodeEncodeError: 'ascii' codec can't encode character u'\u6708' in position 2: ordinal not in range(128)

3. 解决方案一:设置运行时编码为utf-8

.. code:: python

代码语言:javascript
复制
#coding:utf-8
from __future__ import unicode_literals
import sys
from datetime import datetime

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

now = datetime.now()
print now.strftime('%m月%d日 %H:%M')

正常执行

4. 解决方案二: 使用byte string

.. code:: python

代码语言:javascript
复制
#coding:utf-8
from __future__ import unicode_literals
from datetime import datetime

now = datetime.now()
print now.strftime(b'%m月%d日 %H:%M')  # 指明为bytearray字符串

# 或者这样也行
t = bytearray('%m月 %d %H:%M', 'utf-8')
print now.strftime(str(t))

5. 总结

这里主要涉及到python中的编码问题,也是很多人在刚接触Python时感到头疼的问题。更多基础的东西,可以到下面的参考链接里看,这里就分析下我的这几段代码。

先来看 第一段代码 ,第一段能成功执行是正常的,因为datetime的strftime函数,接受的参数就是string(注意:string表示字节,unicode表示字符串,见参考1),因此是正常的,strftime接收到string,然后格式化最后返回。

第二段例子 我们引入了来自future的unicode_literals,这个模块的作用就是把你当前模块所有的字符串(string literals)转为unicode。基于这个认识来看代码,虽然我们给 now.strftime 传递的还是一样的参数,但本质已经不同——一个是string(字节)一个是unicode(字符)。而 strftime 能够接收的参数应该是string类型的,那咱们传了一个unicode进去,它必然要转换一下,这一转换就出错了——UnicodeEncodeError。

这个地方应该详细说下,咱们给定了一个unicode字符"月",要被转为string,怎么转呢?这时就得想到ASCII了,这是Python2.7运行时默认的编码环境。所谓"编码"就是用来编码的嘛,于是python就通过ASCII来把unicode转为string,遂,抛错了。

错误的原因在Traceback中详细指明了——咱们传进去的u'\u6708' (也就是"月"字)ascii解释不了。这个符号不在ascii的128个字符表当中,因此就抛错了。关于字符编码方面的内容可以查看参考5。

再来说 第三段代码 ,我们重载了系统的编码环境为utf-8,于是上面的那个问题消失了,简单来说就是utf-8可以表示更多的字符。

最后来看 第四段代码 ,我们通过把字符串定义为byte类型同样解决了那个错误。原理也很简单,就是先把unicode转换为bytes,然后再转为string。这段代码里提供了两种方法,一个是在字符串前加 b 来声明一个bytes(而不是unicode);第二个是对生成的unicode对象通过utf-8进行编码为bytearray,然后转为string。这个问题可以查看参考4和参考6。

上面都是the5fire自己根据资料总结出来的结论,如果有问题欢迎指出。

PS: 同样的问题对于python built-in的getattr方法也适用。

参考资料:

  1. 黄聪:解决python中文处理乱码,先要弄懂“字符”和“字节”的差别
  2. http://docs.python.org/2/library/datetime.html#datetime.date.strftime
  3. http://docs.python.org/2.7/library/functions.html#getattr
  4. http://docs.python.org/2/whatsnew/2.6.html?highlight=bytestring#pep-3112-byte-literals
  5. http://www.cnblogs.com/huxi/articles/1897271.html
  6. http://stackoverflow.com/questions/6269765/what-does-the-b-character-do-in-front-of-a-string-literal

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2014-03-13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 未引入unicode_literals版本
  • 2. 引入unicode_literals
  • 3. 解决方案一:设置运行时编码为utf-8
  • 4. 解决方案二: 使用byte string
  • 5. 总结
  • 参考资料:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档