Python2 编码问题分析

本文浅显易懂,绿色纯天然,手工制作,请放心阅读。

编码问题是一个很大很杂的话题,要向彻底的讲明白可以写一本书了。导致乱码的原因很多,系统平台、编程语言、多国语言、软件程序支持、用户选择等都可能导致无法正确的解析编码。
导致乱码的主要原因可以简单归结于文本的编码方式和解码方式不同导致的。本文将通过在win7(zh-cn)系统下分析python2.7的编解码问题来简单窥探一下编码的冰山一角。

今后遇到编码问题时能够多一点分析解决思路,要是能起到一个抛砖引玉的作用,那就再好不过了。

1.为什么需要编码

物理存储介质上的基本存储单元只能有两种状态,使用0和1区分。一段连续的0,1序列可以表示特定的信息。

理论上任何字符都可以被唯一的一个连续0,1组成的bit序列表示。如果这样的一个bit序列也唯一的代表一个字符,那么我们可以由此建立一个bit序列和字符之间的转换关系。

数字0 - 9和字母a - z(A - Z)等是人类可识别的字符,但是无法存入到计算机的存储介质中。

十进制 二进制(bit 序列) 字符
48 0011 0000 0
65 0100 0001 A
97 0110 0001 a

为此,我们将这些人类可识别的有意义的字符与特定0,1组成的bit序列建立起一一对应的关系。

由字符转成对应的bit序列的过程称为编码,将bit序列解释成对应的字符则称之为解码。

这样的一个bit序列可以用于存盘和网络传输等所有只能使用二进制表示的环境中。当需要阅读文件时再解码并在显示屏上显示出人类可识别的字符。

编码是人和机器之间的传递和表示信息的一种方式。

2.编码方式

字符与bit序列之间的转换随之带来的两个问题是:

  1. 怎么确定每个字符对应的编码?
  2. 每个字符的编码长度应该是多少?

第一个问题可以用一个大家公认的标准来解决。

第二个问题则主要是节约的角度考虑,在可以表示特定字符集的情况下,需要用尽可能短的二进制序列。

问题看似简单,但由于历史原因,不同国家不同语种的编码标准并不相同。而且即使同一个标准也在不断的发展。

我们常见的编码标准ASCII,UTF-8,Unicode,GBK(中文编码标准)等。

编码长度由编码方式决定,如ASCII码表示的字符都是一个字节(8 bits),Unicode编码的字符一般用两个字节。

同一种编码中不同字符的编码长度也可能不同。如UTF-8,对字符编码尽量压缩以节俭空间,是一种“可变长编码”。

既然存在这么多种编码方式,那么对于一段经过编码的二进制序列,如果以其他的编码方式解码,显然会得到错误的解码信息。

这就是我们所遇到的乱码问题。

那有没有一种编码方式可以将世界上所有的可表示字符都赋予一个唯一的编码?Unicode便是这样一种编码方式。

但是表示范围包罗万象的一个代价就是字符编码长度的增加。这样数据传输和存放时占用的网络和空间资源就会更多。

UTF-8是在Unicode基础上发展而来的可变长的编码方式。

Character ASCII Unicode UTF-8 GBK
0 00110000 00000000 00110000 00110000 00110000
a 01100001 00000000 01100001 01100001 01100001
无法表示 01011011 01010111 (u'\u5b57') 11100101 10101101 10010111 ('\xe5\xad\x97') 11010111 11010110 ('\xd7\xd6')

注意:

上面的0是字符0,对应程序中“0”,而不是数字0。

数字是不使用字符编码的,数字可以使用原码、反码和补码表示,在内存中一般使用补码表示,其字节序有大小端模式决定。

3.编码转换

不同的编码有各自的特点,下面是一种可能的字符编辑显示、加载传输和存储对应的各阶段编码。

由于Unicode可以和任何编码相互转换,可以借助Unicode实现不同编码之间的变换。

python2中有两种表示字符串的类型:str 和 unicode。basestring是二者的共同基类。

Unicode对象包含的是unicode字符,而str对象包含的是字节(byte)。

汉字“中”的编码三种编码方式

'\xd6\xd0'(GBK) <==> u'\u4e2d'(UNICODE) <==> '\xe4\xb8\xad'(UTF-8)

在win7 + python2.7的环境下,Python 自带的IDE中输入的中文默认编码方式为GBK

字符串 '3132' 的编码是 '\x31\x32\x33\x34'

对应第二节表中的数值可以清楚的看到相同的字符对应的不同的编码。

也可以说明UTF-8和GBK的编码兼容ASCII。而ASCII表示的字符在Unicode中则需要两个字节表示。

一个以ASCII编码的文档(注意不是ANSI编码)可以使用UTF-8和GBK编码方式打开,而不能使用Unicode。

为了进一步验证,新建notepad++文档,选择编码方式为ANSI(即GBK),用喜欢的输入法以最快的方式键入汉字“中”。

使用HEX-Editor插件查看文档的GBK编码二进制表示:

“中”的UTF-8编码二进制表示:

“中”的Unicode(对应notepad++中的USC-2 Big Endian)编码二进制表示:

和python2中的结果相比可以看出

除了Unicode字符外,文件中存储的二进制数据和对应的编码是一一对应的。而这也验证了先前说的Unicode对象本身包含的并不是字节。

\u是unicode编码的转义字符,上面起始的0xfeff可以看做unicode编码文件的一个起始标记。

例如
Unicode:       前两个字节为FFFE;
Unicode big endian:  前两字节为FEFF;

实际读取文件时会根据文件的前两个字节就可以判定出文件的具体格式。

使用gbk的方式读取utf-8编码文件(将utf-8文件编码方式改为ANSI),将会以GBK的方式解读字utf-8的节序列'\xe4\xb8\xad',结果如下:

反之,使用utf-8的方式读取GBK编码文件(以utf-8的方式解读字GBK的节序列'\xd6\xd0'):

4.文件显示标注编码类型

在python文件中我们可以使用下面的方式标记文件的编码类型:

# -- coding:utf-8 --

# coding:utf-8

关于python文件编码类型的声明可以参考官网: PEP 263

网上有很多人认为上面的编码声明定义了python文件的编码方式,但与其说定义python文件的编码方式,不如说是这仅仅是对python解释器的一种编码方式声明。即python 文件的编码声明 # -- coding: utf-8 -- 只是给python解释器看的。当python解释器执行py文件时会根据这个声明来解析文件编码格式。

也就是说这个声明和文件本身实际的文件编码没有关系。但两者最好保持一致,否者python解释器会以错误的方式去解码文件内容并执行。

上面的文件声明 # -- coding:gbk -- 误导python解释器以gbk的编码方式去解析一个utf-8编码的文件。

实际开发过程中只需要统一将文件声明为utf-8即可。

再解释一下文件相关的编码概念:

文件本身的编码方式,该编码决定了文件数据以怎样的二进制格式保存在存储介质中。

文件内容显示的格式(解码方式),该编码方决定了文件内容是以怎样的解码方式被显示出来的。

一般文本显示类的程序打开一个文本时会根据文本文件起始字节判断该文本的编码方式,当然也可以有用户手动改变文件的解码方式。

如果文本程序或用户选择和编码方式不兼容的错误的解码方式,可能就会出现乱码。

但是对于可执行py文件而言,python解释器会以py文件开头的编码声明方式来解释文件内容,如果没有声明,python解释器会以默认的ASCII编码解析文件。

也就是只要python可执行文件的声明编码只要和实际文件的编码方式一致,就没有任何问题。因此可以使用# -- coding:gbk --作为声明,但考虑到兼容性和可移植性,最好编码和声明均采用utf-8。

以上结果均在特定的平台和环境下得出,仅为个人见解。如有错误欢迎指正,共同探讨学习。

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

推荐阅读更多精彩内容

  • 《前任3》:没有人会一直等你! 01
    石成玉_d24e阅读 208评论 0 0
  • 看经济学书要读出来 小声读,快读 2.自己说话好多时间就是没逻辑的主要问题就是小逻辑结构不总结,就是眉毛胡子一把抓...
    智囊团阅读 147评论 0 0
  • 文/羽蒙 二十九个岁月里像飘荡的幽灵执着 记忆 疲惫无处安放的思绪曾埋在泥土里 我把执念的光明放在心中连同安宁...
    羽蒙1阅读 489评论 15 11