Python字符集问题解析

概述

不管是哪种语言,大家应该都被字符集困扰过吧...对于Python开发而言,Python2和Python3的字符类型是不同的,Python3做了改变,所以理解起来可能更困难了。

正文部分:

要了解字符集,我们首先得知道在计算机的数据,所有的数据都是用二进制的形式进行存储的。而人们现实中使用的语言有很多,如:英语,中文和日语等等。那么把人类使用的语言翻译成计算机能懂的语言,就是字符集做的事情

我们知道计算机是由英语国家发明的。所以最开始的时候,那帮老外发明了一种叫做ASCII的字符集。ASCII编码把128个英文的字符和符号做了编号映射到计算机的存储单元里(使用一个byte的后7个bits)。比如A 用65表示,a用97表示。由于只占用一个byte,所以ASCII用一个字节来存储一个字符。

但是随着计算机的使用国家增多,单纯的ASCII编码就满足不了其他使用非英语国家的需求了,比如中国和日本。为了解决这个问题,各个国家又分别推出了自己国家语言的字符集:中文(GBK,GB2312);日文(JIS).但是这样做显然是有问题的,想象一个场景,如果一个网页里既有中文又有日文还有英文,像上述的字符集就无法表示这些文字了,这也就是所谓的乱码

为了解决上述问题,一个终极解决方案字符集诞生了,它就是:Unicode。这种编码对世界上大部分的文字系统进行了整理和编码,对于各个国家的各种字符提供了一个统一的数字进行表示,解决了字符集的问题。

UTF-8

Unicode编码范围是0~0x10FFFF,这么大的表示范围意味它无法用一个字节去存储,所以Unicode制定了各种存储编码格式,也就是我们常听到的UTF-8(Unicode转换格式:Unicode Transformation Format,UTF),UTF-16和UTF-32等等。这些数字代表的是Unicode转换格式把编码存储为一到多个单元。UTF-8的编码单位是8bit的字节,UTF-16是16位,UTF-32是32位。
实际中,我们最经常使用的编码方式是UTF-8

Python字符集会出现的问题

了解了字符集,下面我们来看下Python的字符集会出现的问题。

In [33]: sys.version
Out[33]: '2.7.10 (default, Jul 15 2017, 17:16:57) \n[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)]'

In [34]: a = u'字符集'
Out[34]: u'\u5b57\u7b26\u96c6'
In [37]: print a
字符集

在Python2的版本,如果我们想要使用unicode编码,需要在字符前加一个u。此时,如果想保存这个变量,可能会出现什么问题呢?

In [38]: with open('./test.txt', 'w') as f:
    ...:     f.write(a)
    ...:
---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)
<ipython-input-38-3f33d2572bb0> in <module>()
      1 with open('./test.txt', 'w') as f:
----> 2     f.write(a)
      3

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

可以看到,解释器报错,原因是它默认是存储ASCII编码的,但是ASCII显然无法存储汉字,所以抛出了异常。
那如果不用Unicode表示呢?

In [39]: a = '字符集'

In [40]: with open('./test.txt', 'w') as f:
    ...:     f.write(a)

现在我们不用Unicode了,这样虽然可以直接存储,但是做一些切片操作时无法准确完成。

In [41]: a
Out[41]: '\xe5\xad\x97\xe7\xac\xa6\xe9\x9b\x86'

In [42]: print a[:2]
�

Python中的字符集

要解决上面的字符集问题,首先要记住Unicode是表现形式,UTF-8是存储形式UTF-8解码变成Unicode,Unicode编码成为UTF-8(编码成二进制数据,原始的字节)
然后需要记住两个版本的Python字符类型的差异:
Python3有两种字符表现类型:bytes和str。bytes指原始字节(8个二进制位),str指Unicode字符。
Python2有两种字符表现类型:str和Unicode。str指原始字节(8个二进制位),Unicode指的是Unicode字符。

在编写Python的过程中,一定要把编码和解码操作放到界面的最外围来做。程序的核心部分应该使用Unicode字符集(Python2中的Unicode,Python3中的str)。这样做的话,可以让程序接受多种类型的文本编码(GBK, JIS等),也可以保证输出文本信息只用一种编码形式(UTF-8)。

下面让我们来看两个函数:encode()decode()
贴一段代码来看下如何使用(Python2环境下):

In [50]: a
Out[50]: '\xe5\xad\x97\xe7\xac\xa6\xe9\x9b\x86'

In [51]: b = a.decode('utf-8')

In [52]: b
Out[52]: u'\u5b57\u7b26\u96c6'

In [53]: print b
字符集

In [54]: b.encode('utf-8')
Out[54]: '\xe5\xad\x97\xe7\xac\xa6\xe9\x9b\x86'

所以对于上面保存错误,我们可以这样来做:

In [39]: a = u'字符集'

In [40]: with open('./test.txt', 'w') as f:
    ...:     f.write(a.encode('utf-8'))

而在程序编写中,我们要使用Unicode编码,就是说,如果读取这段数据,要进行解码:

In [61]:  with open('./test.txt', 'r') as f:
    ...:      data = f.read()
    ...:

In [62]: data.decode('utf-8')
Out[62]: u'\u5b57\u7b26\u96c6'

可以编写这样的函数,达到自动编码解码的目的:

Python3中,接收str或者bytes,但是总返回str,这个函数用于程序核心部分

def to_str(bytes_or_str):
    if isinstance(bytes_or_str, bytes):
        value = bytes_or_str.decode('utf-8')
    else:
        value = bytes_or_str
    return value

存储时,用这个函数:

def to_bytes(bytes_or_str):
    if isinstance(bytes_or_str, str):
        value = bytes_or_str.encode('utf-8')
    else:
        value = bytes_or_str
    return value 

Python2中,对应的函数如下:

def to_unicode(unicode_or_str):
    if isinstance(unicode_or_str, str):
        value = unicode_or_str.decode('utf-8')
    else
        value = unicode_or_str
    return value

和:

def to_str(unicode_or_str):
    if isinstance(unicode_or_str, unicode):
        value = unicode_or_str.encode('utf-8')
    else
        value = unicode_or_str
    return value

Python3中需要注意的地方

Python3内置的open()函数获取文件的句柄时,会默认采用UTF-8编码格式来操作文件。而在Python2中,默认编码格式是二进制格式。这就会出现一些问题,比如说要向文件中写入二进制数据,在Python3中这样写就会出错:

In [12]: import sys

In [13]: sys.version
Out[13]: '3.6.3 (default, Oct  4 2017, 06:09:38) \n[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.37)]'

In [14]: import os

In [15]: with open('./rand.bin', 'w') as f:
    ...:     f.write(os.urandom(10))
    ...:
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-01652b9c0df8> in <module>()
      1 with open('./rand.bin', 'w') as f:
----> 2     f.write(os.urandom(10))
      3

TypeError: write() argument must be str, not bytes

发生异常的原因在于:python3给open函数添加了encoding这样一个新参数,这个参数的默认值是'utf-8',所以,开发需要传入str格式的数据,而不是bytes格式的数据。

为了解决这个问题,我们需要用二进制模式进行写入('wb'),读的时候类似,用('rb')进行读。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容

  • 字符集和编码简介 在编程中常常可以见到各种字符集和编码,包括ASCII,MBCS,Unicode等字符集。确切的说...
    兰山小亭阅读 8,461评论 0 13
  • 可以看我的博客 lmwen.top 或者订阅我的公众号 简介有稍微接触python的人就会知道,python中...
    ayuLiao阅读 3,097评论 1 5
  • 前言 最先接触编程的知识是在大学里面,大学里面学了一些基础的知识,c语言,java语言,单片机的汇编语言等;大学毕...
    oceanfive阅读 3,048评论 0 7
  • 孩子的政治课本第一课讲到了归属感,孩子在开学前一周就跟我说想学校了,应该就是那种感觉吧。孩子与老师和同学们在一起学...
    心语_rxf阅读 361评论 0 1
  • 01 前些天,男友对我说:“你现在辞职了,要不然我给你买张票,你自己先提前回去吧?” “为什么?”我问道。 “我...
    烟雨红晨阅读 887评论 21 18