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再进行操作,然后输出的时候再编码。

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

推荐阅读更多精彩内容

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