python编码问题

阅读python源代码的时候不难发现许多文件开头都有这样一行内容:

# -*- coding: UTF-8 -*-

如果想在python中使用中文,这样的一行声明也是必须的。python中的编码在实际使用的时候也是经常让人感觉很混乱。以下详细研究一下python的编码问题。

来源

python在上世纪九十年代研发发布,那时候研发者很显然没有想到语言有严重的编码问题。因此在python3之前,python编码默认使用的是ascii。因此使用python自带的str的时候,实际上我们用的是默认的ascii编码方式。

使用# -*- coding: UTF-8 -*-这段代码用于声明代码中文本的编码是utf-8。告诉解释器将文件中的文本视为utf-8编码的字符串。当然使用的时候应该保证代码文件确实是以utf-8的形式存储的。

查看python的默认编码方式可以使用以下语句:

import sys
sys.getdefaultencoding()

python2默认使用的是asciipython3使用的是utf-8

扩展

编码发展历程

由于计算机首先在美国出现,编码最开始只需要表达出英文即可。因此首先出现的编码由8位组成一个字符。有256种状态。其中0到32表示特殊用途的字符,比如回车,响铃等等,用于控制当时的机器的行为。之后是空格标点数字字母等等。一直到127号。这个方案叫做ANSIascii(American Standard Code for Information Interchange)编码。

之后由于世界各地开始使用计算机,很多字符无法使用acsii表示,因此开始采用127号之后的状态表示字符。这些字符被称为扩展字符集

对于中国来说,由于没有足够的字节状态表示汉字,所以取消了之前127号之后的扩展字符集,用两个字节来表示汉字。当一个字节小于127的时候,还是表示之前的ascii字符,当大于127的时候,两个字节在一起表示一个中文字符。前面一个字节(高字节)范围是0xA10xF7,后面一个字节(低字节)范围是0xA10xFE。这些多出来的组合用来表示简体汉字外,还将之前的数学符号,罗马希腊字母,日文假名以及之前的acsii中的内容都编制进去了。这些两位字节的字符叫做全角字符,原来的127号以下的字符叫做半角字符。这个汉字方案叫做GB2312

之后,由于中国汉字太多,GB2312方案也已经不够用来表示所有的汉字。所以不仅将之前没有用完的码位拿出来用上,还取消了低字节一定是127号之后的规定。只要第一个字节是大于127号的就表示这是一个汉字的开始。这个扩展之后的编码方案称为GBK标准。GBK标准增加了包括繁体字等等近20000个新的汉字和符号。

接下来,由于需要加入少数民族的文字,这个标准再次扩充,新加入了几千个少数民族文字,扩展成了GB18030

之前的这一系列汉字标准被称作DBCS(Double Byte Character Set)双字节字符集。在DBCS系列标准里,双字节长的汉字字符和单字节的英文字符并存,因此计算字符串的长度的时候一个汉字等于两个英文字符。

以上是中国的编码标准。由于之前没个国家都按照这个过程制定了一套自己的标准,因此这些编码互相不能相通,甚至台湾也使用了自己的一套DBCS系统GIG5。所以ISO(国际标准化组织)提出了一种新的方案。废除了所有的地区性编码方案。制定了一套包含地球上所有文化字幕和符号的编码,Universal Multiple-Octet Coded Character Set,简称UCS,俗称unicode

这时由于储存器空间的极大发展,因此空间不再成为问题了。ISO规定unicode必须使用两个字节16位来表示所有的字符。之前acsii里面的字符,也只是原编码不变,扩展为16位。因此这种方案会在保存英文文本的时候浪费一半的空间。此时,一个汉字和一个英文都是一个字符,不再像之前一样一个汉字是两个英文字符了。一个字符就是两个字节。

但是unicode有很大的缺点。之前提到的字节浪费就是其中之一。另外当不知道编码方式的时候也很难区分acsii码和unicode码。

直到互联网的出现。由于网络传输再一次对编码精炼程度要求很大,为了解决unicode在网上的传输问题,制定了许多UTF(UCS Transfer Format)标准。其中之前提到的utf-8就是每次传输8个位,utf-16就是每次传输16个位。utf-8时网络上使用最广的unicode传输实现方式。utf-8的特点在于它是一种变长的编码方式。使用一到四个字节表示一个符号,根据不同的符号变化字节长度。当字节在ascii范围内的时候,用一个字节表示,这样就解决了英文的存储浪费和无法区分asciiunicode的问题。对于中文来说unicode中中文一个字节,utf-8中一个中文三个字节。从unicodeutf-8需要经过一系列算法和规则。

python中字符串的编码

之前解释了python明文表示使用utf-8来读取文件的作用。这只是解决了python代码读取的问题。python本身的字符串使用也相当混乱(对于3之前)。由于python使用acsii作为编码方式,因此代码里面的字符串默认的格式是ascii。这样在包含中文等字符的操作的时候,由于编码问题就会报错。python3默认的编码方式已经变成了utf-8所以不会有这些问题。以下都是针对python2来说。

python中的字符串有两种,编码之前的unicode和编码过后的str类型。unicode即字面上的编码方式,使用unicode,按照上文说的,中文两个字节。str可以使用不同的编码方式,比如asciigbkutf-8等等。默认使用的是ascii码。

在上面两种模式中,unicode作为在str的各种编码类型之间转换的中间码形式存在。要想在不同的编码之间互相转化,需要先转化成unicode,之后在转化成目的编码。例如:

testStr = "hello"
print type(testStr)                 
# <type 'str'>
print type(testStr.decode("ascii")) 
# <type 'unicode'>
print type(testStr.decode('ascii').encode('utf-8'))
# <type 'str'>

以上过程即将一个ascii编码的字符串转化成unicode又转化成了utf-8。当然,对于hello这串西文字符来说,这两种编码方式没有区别。

虽然如问题中的声明方式可以将代码文件中的编码方式确定为utf-8。但是代码中涉及到文件处理,网络流处理的时候,还需要进行适当的编码转化。Python试图进行隐式的的转化,使用之前提到的默认编码进行字节串和unicode之间的转化。在默认的编码方式是ascii的情况下,大部分这种转化是错误的。这时候会报这样的错误:

a = '你好'
print a.decode()
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
# 用ascii解码汉字报错
print a.decode('utf-8')
# u'\u4f60\u597d'
print a.decode('utf-8').encode()
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
# 用ascii编码汉字报错
print a.decode('utf-8').encode('utf-8')
# 你好

因此,在使用python2的时候,在编码方面要注意尽量使用unicode来进行字符串的操作。字符串需要使用正确的编码标准来进行解码和编码。从外界读取的字符串都先解码成unicode再进行操作,然后输出的时候再编码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 几个基本概念 bit二进制位, 是计算机内部数据储存的最小单位,11010100是一个8位二进制数。一个二进制位只...
    西电大侠阅读 3,590评论 1 8
  • 什么是编码 任何一种语言、文字、符号等等,计算都是将其以一种类似字典的形式存起来的,比如最早的计算机系统将英文文字...
    随风化作雨阅读 1,562评论 1 2
  • 很久没有码字的冲动了。记忆中还在大学,每天晚上,搬个小凳,在宿舍门口,正襟危坐,像模像样,复习一天的功课之余,总是...
    十点洞见阅读 2,189评论 0 4
  • 这两天没啥胃口晚上就想着煮些粥来吃顺便再加一个水煮蛋。没想到感觉特别好吃,其实煮的粥也是普普通通的大米,...
  • 2018-9-16 姓名:张文清 公司:宁波大发化纤有限公司天】 【知~学习】 《六项精进》大纲背诵1)遍 通篇朗...
    z张文清阅读 191评论 0 0