专栏首页Python空间不要再问我 Python2 和 Python3 的 Unicode 问题啦!

不要再问我 Python2 和 Python3 的 Unicode 问题啦!

本文字数:2579 字 阅读本文大概需要:7 分钟

写在之前

字符编码问题几乎是会跟随我们整个编程生涯的一大魔障,一不小心各种玄学的问题就会接踵而至,防不胜防,尤其是对初学者来说,碰到编码问题简直是就是加快了踏上从入门到放弃的传送带。

鉴于我公众号的读者初学者占了一大部分,所以很多时候我会收到各种询问字符编码的问题,这里面有用 Python2 的,也有用 Python3 的,鉴于在编码问题上这两种版本的 Python 有着很大的不同,所以在这篇文章中我都会讲到。

在往下看之前,希望你先了解一下编码,字符编码以及其发展史这些概念,我在很久之前的文章里写过(零基础学习 Python 之字符编码),不了解的可以看一下。

字符集问题

很多时候在使用 Python 编程的时候,如果不使用 Unicode,处理中文的时候会出现一些让人头大的事情,当然这个是针对 Python2 版本来说的,因为 Python3 默认使用的是 Unicode。具体如下所示:

>>> name = '李四'
>>> name
'\xe6\x9d\x8e\xe5\x9b\x9b'
>>> print(name)
李四
>>> len(name)
6
>>> name[:1]
'\xe6'
>>> print(name[:1])
?

通过上面的例子可以看到,我们在代码中使用中文以后,求字符串的长度和对其进行切片操作都没有按照我们预想的方式输出结果,当然有懂得读者知道这个问题用 Unicode 就可以轻松解决,但真的是轻松解决么?如果你对字符集编码只是半瓶子醋,新出现的问题又会让你头大如斗。具体如下所示:

>>> name = u'李四'
>>> name
u'\u674e\u56db'
>>> name[:1]
u'\u674e'
>>> print(name[:1])
李
>>> with open('./Desktop/test', 'a') as f:
...     f.write(name)
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

上述的代码出现了错误,报错的原因很简单,因为我们定义了一个 Unicode 字符串 u'李四',然后我们想把它保存到文本文件里,但是我们没有指定文件的编码,所以默认的是 ASCII 编码,显然用 Unicode 表示的汉字是无法用 ASCII 码存储的,所以就抛出了 UnicodeEncodeError 异常。

Python2 & Python3 的 Unicode

前面铺垫的够多,现在我们算是正式来看 Python 中的字符串与字符编码之间的调用。

首先来说 Python3,Python3 里有两种表示字符序列的类型,分别是 bytes 和 str,bytes 的实例包含 8 位值,str 的则包含 Unicode 字符。Python2 中也有两种表示字符序列的类型,分别是 str 和 Unicode,它与 Python3 的不同是,str 的实例包含原始的 8 位值,而 Unicode 的实例包含 Unicode 字符。这是什么意思呢?也就是说 Python3 中字符串默认为 Unicode,但是如果在 Python2 中需要使用 Unicode,必须要在字符串的前面加一个 「u」前缀,形式参考上面例子中的写法。

当然了,在 Python2 中也可以默认使用 Unicode 的字符串,执行下面的操作即可:

from __future__ import unicode_literals

Python 字符串有 encode 和 decode 方法,用这两个可以对字符串进行编码或解码,我们来看一个在 Python2 下运行的例子:

>>> name = '李四'
>>> name
'\xe6\x9d\x8e\xe5\x9b\x9b'
>>> my_name = name.decode('utf8')
>>> my_name
u'\u674e\u56db'
>>> print(my_name)
李四
>>> my_name.encode('utf-8')
'\xe6\x9d\x8e\xe5\x9b\x9b'

既然我们知道了 encode 用于编码,decode 用于解码,那么对于之前我们抛出异常的那个例子我们可以手动解决,具体如下所示:

>>> with open('./Desktop/data.txt', 'a') as f:
...     f.write(name.encode('utf-8'))
... 
>>> with open('./Desktop/data.txt', 'r') as f:
...     data = f.read()
... 
>>> data.decode('utf-8')
u'\u674e\u56db'

上述代码是字符串较短的情况,如果需要写入的字符串很多,每次都要手动进行编码将会变的非常低效,Python2 中有个「codecs」模块可以解决这个问题:

>>> import codecs
>>> name
u'\u674e\u56db'
>>> with codecs.open('./Desktop/data.txt', 'w', encoding='utf-8') as f:
...     f.write(name)
... 
>>> with codecs.open('./Desktop/data.txt', 'r', encoding='utf-8') as f:
...     data = f.read()
... 
>>> data
u'\u674e\u56db'

而在 Python3 中内置的 open 就已经支持指定编码风格:

>>> name = '李四'
>>> name
'李四'
>>> with open('./Desktop/data.txt', 'w', encoding='utf-8') as f:
...     f.write(name)
... 
2

把 Unicode 字符表示为二进制的数据有很多种办法,最常见的就是 utf-8,但是这里需要我们明白的是,Unicode 是表现形式,utf-8 是存储形式,utf-8 虽然是使用最广泛的编码,但也仅仅是 Unicode 的一种存储形式罢了。

当然字符编码的问题还有很多,我也不可能面面俱到,最后我还是希望你可以在一开始的时候就彻底的搞定字符编码的东西,拿出一些时间来好好研究一下,不然这个东西会成为你编码路上挥之不去的“噩梦”。

本文分享自微信公众号 - Python空间(Devtogether),作者:Rocky0429

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-12-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 零基础学习 Python 之嵌套函数

    我在几天以前的文章中(零基础学习 Python 之函数对象)说过,函数不单单可以作为对象来传递,还可以在一个函数里面嵌套一个函数,这个就是我们今天要讲的嵌套函数...

    Rocky0429
  • 零基础学习 Python 之封装

    今天来讲 OOP 的最后一个特性:「封装」。封装是对具体对象的一种抽象,简单来说就是将某些部分隐藏起来,在程序外部看不到,这个看不到不是说人用眼睛看不到那个代码...

    Rocky0429
  • 零基础学习 Python 之继承(一)

    面向对象的程序设计都三个主要的特征:封装,继承,多态,这个也是类里面的重要内容,这三个特征我会从今天开始依次开始写,今天我们先来看第一个:「封装」,这一部分我会...

    Rocky0429
  • 27.python __name__ == ‘__main__’详细解释

    学习过C语言或者Java语言的盆友应该都知道程序运行必然有主程序入口main函数,而python却不同,即便没有主程序入口,程序一样可以自上而下对代码块依次运行...

    猿说编程[Python和C]
  • python __name__ == ‘__main__’详细解释

    学习过C语言或者Java语言的盆友应该都知道程序运行必然有主程序入口main函数,而python却不同,即便没有主程序入口,程序一样可以自上而下对代码块依次运行...

    猿说编程[Python和C]
  • Python小知识点(4)--模块相关

    定义:用来从逻辑上组织python代码(变量,函数,类,逻辑:实现一个功能),本质就是以.py结尾的python文件(文件名:test.py,对应的模块名:te...

    wfaceboss
  • python3 __name__

    所以我们平常见到的__name__ == "__main__"就是指当前文件作为脚本运行时会发生的一些事情, 防止当前文件作为模块导入时运行自己不想运行的代码

    py3study
  • vue路由跳转传参数

    1. router-link <router-link :to="{ path: 'yourPath', param...

    庞小明
  • 使用ES6新特性开发微信小程序(3)——类

    Class(类) Class是ES6引入的最重要特性之一。在没有Class之前,我们只能通过原型链来模拟类。 Class Definition(类的定义) cl...

    极乐君
  • python 2 :包管理

    py3study

扫码关注云+社区

领取腾讯云代金券