前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python中GBK, UTF-8和Unicode的编码问题

Python中GBK, UTF-8和Unicode的编码问题

作者头像
bear_fish
发布2018-09-20 14:41:50
4K0
发布2018-09-20 14:41:50
举报
文章被收录于专栏:用户2442861的专栏

编码问题,一直是使用python2时的一块心病。几乎所有的控制台输入输出、IO操作和HTTP操作都会涉及如下的编码问题:

代码语言:javascript
复制
UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xc4 in position 10: ordinal not in range(128)

这究竟是是个什么东西?!有时稀里糊涂地用一坨encode(),decode()之类的函数让程序能跑对了,可是下次遇到非ASCII编码时又悲剧了。

那么Python 2.x中的字符串究竟是个什么呢?

基本编码知识

在了解Python中字符串(String)的本质前,我们需要知道ASCII、GBK、UTF-8和Unicode的关系究竟几何。 我们知道,任何字符串都是一串二进制字节的序列,而ASCII码是最经典的编码方式,它将序列中的每个字节理解为一个字符,可表示阿拉伯数字、字母在内的128个不同字符。很明显,汉字在ascii中是无法表示的。 为了让计算机能够显示、处理汉字,勤劳朴实的中国人民制定了GBK(GB2312的扩展)编码,这是一种兼容ASCII的不定长(长度为1-2)编码,对于基本的128个字符仍旧用一个字节表示,但“翔”这样的中文就用两个字节表示:

UTF-8与GBK类似,也是一种兼容ASCII码的不定长编码形式,它的长度变化更大,因此可以表示几乎所有世界文字。具体细节可参考维基:http://zh.wikipedia.org/wiki/UTF-8

Unicode是一种定长的编码方式(同ASCII),不过它是每2字节认为是一个字符,如ASCII中0x61表示'a',在Unicode中用0x0061表示'a',它可映射所有文字,而且对于多种写法的字,如強/强,它都可以唯一地区分它们。

由于Unicode编码的字符串体积很大,因此一般来说Unicode编码只是文字在内存中的内在形式,具体的存储(如文件、网页等)都需要靠外在的编码(UTF-8、GBK等)诠释。

Python2.x中字符串的本质

Python中实际上有两种字符串,分别是str类型和unicode类型,这两者都是basestring的派生类。它们的区别如下:

字符串类型

常量子串表示

内存中表示

len()

len含义

str

S=“呵呵”

与源码文件完全一致,一坨二进制编码

若源码文件为UTF-8编码, len(S)=6

字节数

unicode

S=u“呵呵”

Unicode编码

len(S)=2

字数

str类型的本质就是一坨二进制串,源文件(或获取的网页)的编码是怎样,它就跟着是怎样。实际上Python并不清楚某个str字符串到底是什么编码。这也就解释了为什么我们需要在python文件的开头标定该文件的编码是什么,如:

代码语言:javascript
复制
# encoding: utf-8

也解释了为什么len()一个str类型的字符串,只会返回它在内存中占用的字节数,而非文字数。 相比于str,unicode是真正的字符串。Python明确地知道它的编码,所以可以很自信地获得一个字符串的实际字数。

字符串编码转换:encode()和decode()

Python最常用的编码转换函数是encode()和decode(),他们的本质是:unicode和str的互相转换。 具体而言: encode(encoding): 将unicode转换为str,并使用encoding编码; decode(encoding):将str转换为unicode,其中str以encoding编码。

我们来看一个例子:

代码语言:javascript
复制
#encoding: utf-8
s = "你好" # 整个文件是UTF-8编码,所以这里的字符串也是UTF-8
u = s.decode("utf-8") # 将utf-8的str转换为unicode
g = u.encode('GBK') # 将unicode转换为str,编码为GBK
print type(s), "len=", len(s) 
# 输出: len= 6,utf-8每个汉字占3字节
print type(u), "len=", len(u) 
# 输出: len= 6,unicode统计的是字数
print type(g), "len=", len(g) 
# 输出:g = u.encode('GBK'),GBK每个汉字占2字节
print s 
# 在GBK/ANSI环境下(如Windows),输出乱码,
#因为此时屏幕输出会被强制理解为GBK;Linux下显示正常
print g 
# 在Windows下输出“你好”,
#Linux(UTF-8环境)下报错,原因同上。

在Windows7(中文)下运行结果如下:

代码语言:javascript
复制
 len= 6
 len= 2
 len= 4
浣犲ソ
你好
Traceback (most recent call last):
  File "C:/Users/Sunicy/Desktop/encode.py", line 15, in 
    g.decode('utf-8')
  File "C:\Python27\lib\encodings\utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0xc4 in position 0: invalid continuation byte

判断变量是否为字符串

我们知道Python中判断一个变量是否为某个类型使用isinstance(变量, 类型)函数,如

代码语言:javascript
复制
isinstance(1.2, float)

返回值为True

那么判断变量是不是字符串能不能用

代码语言:javascript
复制
isinstance(s, str)

呢?

答案是否定的。 现在我们知道除了str之外,unicode类型也是字符串,因此上述代码如果遇到unicode字符串,就返回False。 直观地改进是既判断str又判断unicode:

代码语言:javascript
复制
isinstance(s, str) or isinstance(s, unicode)

不过这个方法有效,但是有点傻。既然str和unicode都派生自basestring,那么实际上以basestring作为类型是最稳妥的:

代码语言:javascript
复制
isinstance(s, basestring)

下面是一组例子:

代码语言:javascript
复制
isinstance("aaa", str) # -> True
isinstance({}, dict) # -> True
isinstance([1,], list) # -> True
isinstance("aaa", list) # -> False
isinstance("你", str) # -> False
isinstance("你好", basestring) # -> True
isinstance("aaa", basestring) # -> True

总结

  1. unicode是支持所有文字的统一编码,但一般只用作文字的内部表示,文件、网页(也是文件)、屏幕输入输出等处均需使用具体的外在编码,如GBK、UTF-8等;
  2. encode和decode都是针对unicode进行“编码”和“解码”,所以encode是unicode->str的过程,decode是str->unicode的过程;
  3. unicode和str是一对孪生兄弟,来自basestring,所以用isinstance(s, basestring)来判断s是否为字符串。

原文地址:Python中GBK, UTF-8和Unicode的编码问题, 感谢原作者分享。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015年04月15日,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基本编码知识
  • Python2.x中字符串的本质
  • 字符串编码转换:encode()和decode()
  • 判断变量是否为字符串
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档