彻底搞懂 Python 编码

因为中文的特殊编码,导致 Python2 和 Python3 使用过程中的各种编码问题,如果不清楚其中的关联关系,那么这就一直是个大坑,不是懵逼就还是懵逼,所以就目前碰到的情况彻底梳理下 Python2 和 Python3 中编码的关系和区别,以作备忘。

先说下涉及编码格式的几个地方:

  1. 脚本字符编码:就是经常在脚本文件开头看到的 # -*- coding: utf-8 -*-,如果使用 Python2,没有显式声明的话默认使用 ASCII 格式,Python3 默认使用 utf-8 格式;
  2. 解释器字符编码:可以通过函数 sys.getdefaultencoding() 查看,Python2 默认是 ASCII,Python3 默认使用 utf-8;
  3. 脚本文件存储编码:就是 py 脚本文件本身在物理介质上面的存储格式,通常有 ASCII、GBK、utf-8 等格式。

下面我们把上述编码分别在脚本中进行组合使用后,再使用 Python2.6 和 Python3.4 运行,看看实际都什么效果。

1.默认脚本文件编码 + 文件存储使用 gbk

脚本内容:

import sys

print(sys.getdefaultencoding())
print(‘中文‘)

使用 Python2.6 运行的结果如下,提示gbk 编码字符 \xd6 非 ASCII 字符:

> python26 test_gbk.py
  File "test_gbk.py", line 4
SyntaxError: Non-ASCII character ‘\xd6‘ in file test_gbk.py on line 4, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

使用 Python3.4 运行的结果如下,提示gbk 编码字符 \xd6 非 utf-8 字符:

> python26 test_gbk.py
  File "test_gbk.py", line 4
SyntaxError: Non-UTF-8 code starting with ‘\xd6‘ in file test_gbk.py on line 4, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

结论:默认的 gbk 编码中文,Python2的解释器字符编码(ASCII)和 Python3的解释器字符编码(utf-8)格式都没法识别,因为 ASCII 编码不包含中文,而 utf-8 是 3 字节编码,gbk 是 2 字节编码,所以都识别不了了。

2.脚本文件编码 gbk + 文件存储使用 gbk

在刚才的脚本头部显式声明脚本文件编码格式为 gbk:

#coding:gbk

import sys

print(sys.getdefaultencoding())
print(‘中文‘)

使用 Python2.6 运行的结果:

> python26 test_gbk.py
ascii
中文

使用 Python3.4 运行的结果:

> python34 test_gbk.py
utf-8
中文

结论:文件使用的 gbk 格式存储,同时显式声明了脚本文件编码为 gbk,Python2 和 Python3 都可以正常处理。

3.脚本文件编码 utf-8 + 文件存储使用 gbk

在刚才的脚本头部显式声明脚本文件编码格式为 utf-8:

# -*- coding: utf-8 -*-

import sys

print(sys.getdefaultencoding())
print(‘中文‘)

使用 Python2.6 运行的结果正常:

> python26 test_gbk.py
ascii
中文

使用 Python3.4 运行的结果如下,提示尝试使用 utf-8 解码字符 0xd6 时异常:

> python34 test_gbk.py
File "test_gbk.py", line 6
SyntaxError: (unicode error) ‘utf-8‘ codec can‘t decode byte 0xd6 in position 0: invalid continuation byte

结论:文件使用的 gbk 格式存储,同时显式声明了脚本文件编码为 utf-8时,但是 Python2 在 Windows 平台还是使用 gbk 进行输出,所以解析正常,而 Python3 使用 utf-8 所以解析异常。

4.默认脚本文件编码 + 文件存储使用 utf-8

去掉之前脚本头部的声明,然后使用 utf-8 格式存储文件(注意,不能在刚才的文件基础上强制修改存储编码,强制转换会出现中文乱码的问题,建议先新建一个 utf-8 格式的文件,然后再输入中文):

import sys

print(sys.getdefaultencoding())
print(‘中文‘)

使用 Python2.6 运行的结果如下,ASCII 也识别不了 utf-8 格式的字符 \xe4

> python26 test.py
  File "test.py", line 4
SyntaxError: Non-ASCII character ‘\xe4‘ in file test.py on line 4, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

使用 Python3.4 运行的结果,可以正常识别,因为 Python3 默认使用 utf-8 编码:

> python34 test.py
utf-8
中文

结论:默认的 utf-8 编码中文,Python2 会默认使用 ASCII 读取,所以没法识别,Python3 可以正常识别。

5.脚本文件编码 gbk + 文件存储使用 utf-8

脚本头部显式声明脚本文件编码格式为 gbk,同时使用 utf-8 格式存储文件:

#coding:gbk
import sys

print(sys.getdefaultencoding())
print(‘中文‘)

使用 Python2.6 运行的结果如下,使用 gbk 根本读取不了 utf-8 格式任何内容:

> python26 test.py
File "test.py", line 6
SyntaxError: ‘gbk‘ codec can‘t decode bytes in position 9-10: illegal multibyte sequence

使用 Python3.4 运行的结果如下,其实和上面错误一样,但是提示更直接了:

> python34 test.py
File "test.py", line 1
SyntaxError: encoding problem: gbk

结论:默认的 utf-8 编码中文,如果显式指定使用 gbk 读取,Python2 和 Python3 都没法做到。

6.脚本文件编码 utf-8 + 文件存储使用 utf-8

脚本头部显式声明脚本文件编码格式为 utf-8,同时使用 utf-8 格式存储文件:

# -*- coding: utf-8 -*-
import sys

print(sys.getdefaultencoding())
print(‘中文‘)

使用 Python2.6 运行的结果如下,虽然读取正确了,但是 Python2 在 Windows 系统会默认使用 gbk 对中文进行解码,所以输出乱码:

> python26 test.py
ascii
涓枃

使用 Python3.4 运行的结果正常:

> python34 test.py
utf-8
中文

结论:虽然文件存储编码和脚本文件编码都是 utf-8,但是 Windows 平台上,Python2 会按 gbk 解析中文,所以会输出乱码,可以在中文前面加 u 来解决u‘中文‘,或者显式使用 utf-8 进行一次 decode。

汇总下验证结果,可以得到如下的表格:

不同组合下 Python3 和 Python2 处理结果 Python3 Python2
默认脚本文件编码 + 文件存储使用 gbk SyntaxError,解析错误 SyntaxError,解析错误
脚本文件编码 gbk + 文件存储使用 gbk 正常输出中文 正常输出中文
脚本文件编码 utf-8 + 文件存储使用 gbk SyntaxError,解析错误 正常输出中文
默认脚本文件编码 + 文件存储 utf-8 正常输出中文 SyntaxError,解析错误
脚本文件编码 gbk + 文件存储使用 utf-8 SyntaxError,解析错误 SyntaxError,编码错误
脚本文件编码 utf-8 + 文件存储使用 utf-8 正常输出中文 中文输出乱码

总结下结论:

  1. 如果使用 Python2 请一定要使用 gbk 格式存储文件;
  2. 如果使用 Python2 尽可能使用 gbk 存储文件且显式声明脚本文件编码为 gbk,方便后续兼容 Python3;
  3. 如果使用 Python3 不管使用什么格式存储文件,但请一定保证显式声明脚本文件编码和存储格式一致;
  4. 不管是使用 Python2 还是 Python3,保持显式声明脚本文件编码的好习惯;
  5. 如果脚本有跨平台需求,推荐使用 Python3 + 脚本文件编码 utf-8 + utf-8 格式存储文件的组合;

原文地址:http://blog.51cto.com/sylan215/2145115

时间: 2024-10-09 02:36:02

彻底搞懂 Python 编码的相关文章

【白话篇】10分钟搞懂字符编码

如上图所示为常见的,让人看了头晕的 几个种编码. 看懂下面几条规则,你就明白他们的关系了. [1]有些人说,GBK严格来说是字符集,而utf-8则是编码,这种区分已经相当模糊了,他们都是"字节到字符的映射关系",所以下面都用编码来说吧. [2] ISO-8859-1 这种编码是单字节编码,衍生于ASCII,表示范围0-255,只要按照ASCII的规则设计的编码,不管是几字节的,都可以和ISO-8859-1兼容. [3]比如说,GBK编码(双字节)能转化成ISO-8859-1编码,是因为

彻底搞懂字符编码(unicode,mbcs,utf-8,utf-16,utf-32,big endian,little endian...)[转]

最近有一些朋友常问我一些乱码的问题,和他们交流过程中,发现这个编码的相关知识还真是杂乱不堪,不少人对一些知识理解似乎也有些偏差,网上百度, google的内容,也有不少以讹传讹,根本就是错误的(例如说 unicode编码是两个字节),各种软件让你选择编码的时候,常常是很长的一个选单,让用户不知道该如何选.基于这样的问题,我就写下我的理解吧,一方面帮助一些需要帮助的人纠正认识,一方面作为自己以后备查的资料. 1. ASCII(American Standard Code for Informati

完全搞懂python打印九九乘法表

说来惭愧,入行1年多一直没去搞懂九九乘法表的第二层for循环,for j in range(1,i+1),它为什么要i+1.直到今天面试官让我说99乘法表实现的具体代码,才忽然之间懂了. 为什么内层for循环需要加1? 原因:外层的range函数(1,10)只能取到9,内层的range(1,i),i只能取到8,因此i需要加1,range(i,i+1),i才能取到9. for i in range(1,10): for j in range(1,i+1): print("{0}*{1}={2}&q

彻底弄懂python编码

在编写python程序的过程中,中英文混用经常会出现编码问题.围绕此问题,本文首先介绍编码的含义及常用编码,随后列举几个python经常遇到的编码异常及解决方法,接着列举笔者在实践中遇到的异常出现的情景及原因,最后针对编码问题提出最佳实践. 一 常见编码 1.1 unicode编码 在文本文件中,看到的所有字符,包括中文,都需要在计算机中存储,而计算机只能存储0和1这样的二进制位,所以需要一种方法,将字符映射成数字,然后将数字转化为二进制位存储在计算机中.针对字符和数字的映射的问题,产生了uni

彻底搞懂Python切片操作

一.Python可切片对象的索引方式 Python可切片对象的索引方式包括:正索引和负索引两部分.如下图所示,以a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]为例: 二.Python切片操作的一般方式 一个完整的切片表达式包含两个“:”,用于分隔三个参数(start_index.end_index.step),当只有一个“:”时,默认第三个参数step=1. 切片操作基本表达式:object[start_index : end_index : step] step:正负数均

帮你搞懂Python进程,线程与协程

本文参考原文-http://bjbsair.com/2020-03-22/tech-info/4425/在操作系统中,每一个独立运行的程序,都占有 操作系统 分配的资源,这些程序中间互不干涉,都只负责运行自己的程序代码,这就是进程. 但是当操作系统频繁的创建销毁进程时,大量的系统资源被浪费在创建和销毁的过程中.而随着多核心 cpu 的出现,线程也逐渐代替了进程,成为了操作系统 可以独立运行的基本单位. 当进程不是多线程程序时,存在于进程当中的唯一线程,便是进程本身运行的代码块. 而当多线程出现在

搞懂python中的可迭代对象和迭代器对象(即迭代器)

可迭代的对象和迭代器解惑: 可迭代的对象:常见的可以被for循环迭代的一些数据类型都是可迭代的对象,如列表,元组,字典,集合,字符串,生成器,range函数生成的数列等,从广泛的意义上来说,这些对象都有一个内置的iter方法,且该方法可以返回一个迭代器对象,当用iter(可迭代对象)调用这个对象时,会返回一个迭代器对象(属于Iterator类) for语句的原理就是先用iter函数获取可迭代对象的迭代器,然后调用next函数,此函数自动调用迭代器对象的next方法,每次遍历都返回相应的值,如果没

真正搞明白Python中Django和Flask框架的区别

在谈Python中Django框架和Flask框架的区别之前,我们需要先探讨如下几个问题. 一.为什么要使用框架? 为了更好地阐述这个问题,我们把开发一个应用的过程进行类比,往往开发一个应用(web应用.系统应用)跟建造房子的过程一样,需要先打地基,搭好骨架,然后一块砖一块砖叠上去. 而开发一个应用呢?同样也需要一个好的架构设计,数据库建模,然后一个模块一个模块使用代码实现. 如果开发一个软件应用不使用框架,和我们建房子时,每一块砖.每一根钢筋都需要自己生产出来本质上是一样的. 显而易见,如果在

PYTHON编码处理-str与Unicode的区别

一篇关于str和Unicode的好文章 整理下python编码相关的内容 注意: 以下讨论为Python2.x版本, Py3k的待尝试 开始 用python处理中文时,读取文件或消息,http参数等等 一运行,发现乱码(字符串处理,读写文件,print) 然后,大多数人的做法是,调用encode/decode进行调试,并没有明确思考为何出现乱码 所以调试时最常出现的错误 错误1 Traceback (most recent call last): File "<stdin>"