据说,每个做 Python 开发的都被字符编码的问题搞晕过,最常见的错误就是 UnicodeEncodeError、UnicodeDecodeError,你好像知道怎么解决,遗憾的是,错误又出现在其它地方,问题总是重蹈覆辙,str 到 unicode 之间的转换用 decode 还是 encode 方法还特不好记,老是混淆,问题究竟出在哪里?

为了弄清楚这个问题,我决定从 python 字符串的构成以及字符编码的细节上进行深入浅出的分析

字节与字符

计算机存储的一切数据,文本字符、图片、视频、音频、软件都是由一串01的字节序列构成的,一个字节等于8个比特位。

而字符就是一个符号,比如一个汉字、一个英文字母、一个数字、一个标点都可以称为一个字符。

字节方便存储和网络传输,而字符用于显示,方便阅读。例如字符 "p" 存储到硬盘是一串二进制数据 01110000,占用一个字节的长度

编码与解码

我们用编辑器打开的文本,看到的一个个字符,最终保存在磁盘的时候都是以二进制字节序列形式存起来的。那么从字符到字节的转换过程就叫做编码(encode),反过来叫做解码(decode),两者是一个可逆的过程。编码是为了存储传输,解码是为了方便显示阅读。

例如字符 "p" 经过编码处理保存到硬盘是一串二进制字节序列 01110000 ,占用一个字节的长度。字符 "禅" 有可能是以 "11100111 10100110 10000101" 占用3个字节的长度存储,为什么说是有可能呢?这个放到后面再说。

Python 的编码为什么那么蛋疼?当然,这不能怪开发者。

这是因为 Python2 使用 ASCII 字符编码作为默认编码方式,而 ASCII 不能处理中文,那么为什么不用 UTf-8 呢?因为 Guido 老爹为 Python 编写第一行代码是在1989年的冬天,1991年2月正式开源发布了第一个版本,而 Unicode 是1991年10月发布的,也就是说 Python 这门语言创立的时候 UTF-8 还没诞生,这是其一。

Python 把字符串的类型还搞成两种,unicode 和 str ,以至于把开发者都弄糊涂了,这是其二。python3 彻底把 字符串重新改造了,只保留一种类型,这是后话,以后再说。

str与unicode

Python2 把字符串分为 unicode 和 str 两种类型。本质上 str 是一串二进制字节序列,下面的示例代码可以看出 str 类型的 "禅" 打印出来是十六进制的 \xec\xf8 ,对应的二进制字节序列就是 '11101100 11111000'。

而 unicode 类型的 u"禅" 对应的 unicode 符号是 u'\u7985'

我们要把 unicode 符号保存到文件或者传输到网络就需要经过编码处理转换成 str 类型,于是 python 提供了 encode 方法,从 unicode 转换到 str,反之亦然。

不少初学者怎么也记不住 str 与 unicode 之间的转换用 encode 还是 decode,如果你记住了 str 本质上其实是一串二进制数据,而 unicode 是字符(符号),编码(encode)就是把字符(符号)转换为 二进制数据的过程,因此 unicode 到 str 的转换要用 encode 方法,反过来就是用 decode 方法。

encoding always takes a Unicode string and returns a bytes sequence, and decoding always takes a bytes sequence and returns a Unicode string".

清楚了 str 与 unicode 之间的转换关系之后,我们来看看什么时候会出现 UnicodeEncodeError、UnicodeDecodeError 错误。

错误日志

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

为什么会出现 UnicodeEncodeError?

因为调用 write 方法时,Python 会先判断字符串是什么类型,如果是 str,就直接写入文件,不需要编码,因为 str 类型的字符串本身就是一串二进制的字节序列了。

如果字符串是 unicode 类型,那么它会先调用 encode 方法把 unicode 字符串转换成二进制形式的 str 类型,才保存到文件,而 encode 方法会使用 python 默认的 ascii 码来编码

但是,我们知道 ASCII 字符集中只包含了128个拉丁字母,不包括中文字符,因此 出现了 'ascii' codec can't encode characters 的错误。要正确地使用 encode ,就必须指定一个包含了中文字符的字符集,比如:UTF-8、GBK。所以要把 unicode 字符串正确地写入文件,就应该预先把字符串进行 UTF-8 或 GBK 编码转换。当然,把 unicode 字符串正确地写入文件不止一种方式,但原理是一样的,这里不再介绍,把字符串写入数据库,传输到网络都是同样的原理

UnicodeDecodeError

把一个经过 UTF-8 编码后生成的字节序列 '\xe7\xa6\x85' 再用 GBK 解码转换成 unicode 字符串时,出现 UnicodeDecodeError,因为 (对于中文字符)GBK 编码只占用两个字节,而 UTF-8 占用3个字节,用 GBK 转换时,还多出一个字节,因此它没法解析。避免 UnicodeDecodeError 的关键是保持 编码和解码时用的编码类型一致。

这也回答了文章开头说的字符 "禅",保存到文件中有可能占3个字节,有可能占2个字节,具体处决于 encode 的时候指定的编码格式是什么。

str 与 unicode 字符串 执行 + 操作是,Python 会把 str 类型的字节序列隐式地转换成(解码)成 和 x 一样的 unicode 类型,但Python是使用默认的 ascii 编码来转换的,而 ASCII 中不包含中文,所以报错了。正确地方式应该是显示地把 y 用 UTF-8 或者 GBK 进行解码。

python语言标号_Python 编码为什么那么蛋疼?相关推荐

  1. python 中文编码差异_Python 编码为什么那么蛋疼?

    平常还觉得编码问题一般能水过去,到爬虫这真是"不信抬头看,苍天饶过谁"... 其实,Python2和Python3中对于编码已经发生了很大的改变(但其实是更方便了),我们一个个来讨 ...

  2. python数字编码_Python 编码为什么那么蛋疼?

    说到底,觉得蛋疼只是因为自己编码知识没学好. 这里一些答案说的不错,但这种问题光从Python2本身的角度去回答意义并不大.如果没有理解字符编码的模型与原理,很难说以后换个语言换个环境就不会重蹈覆辙. ...

  3. python语言核心技术_python核心技术

    基本语法 Python的设计目标之一是让代码具备高度的可阅读性.它设计时尽量使用其它语言经常使用的标点符号和英文单字,让代码看起来整洁美观.它不像其他的静态语言如C.Pascal那样需要重复书写声明语 ...

  4. python语言结构_Python语言表示语句结构时采用

    Python语言表示语句结构时采用 答: 缩进 夏代出现专门的教射和习射的场所是 . 答:序 五行相生相克,又分别对应五种颜色,其中火对应颜色() 答:赤 非暴力沟通中的"表达情绪" ...

  5. python语言做法_python学习笔记(十六)

    ## Python语言进阶 ### 重要知识点 - 生成式(推导式)的用法 ```Python prices = { 'AAPL': 191.88, 'GOOG': 1186.96, 'IBM': 1 ...

  6. python语言用法_python语言基本语句用法总结(1.)

    python语句与语法 1.python简单语句的基本介绍 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21>>> wh ...

  7. python语言画心_python语言还是java如何用python画爱心

    用python绘制爱心的基本步骤如下: 002pc.com对<python语言还是java如何用python画爱心>总结来说,为我们学习Python很实用. 首先先下载安装好python程 ...

  8. python语言编程基础视频_网络编程-5_ Python系列视频(一)——Python语言基础_Python视频-51CTO学院...

    通过学习,对Python有一定的了解,学习Python语法,可以使用Python原生语言开发项目.对于Python的应用于开发有一个系统的认知,对于未来的发展方向有清晰的认识.主要知识点包括基本语法. ...

  9. python语言数据挖掘python语言数据_Python语言数据挖掘01-环境搭建

    本文主要向大家介绍了Python语言数据挖掘01-环境搭建,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助. Python是数据挖掘的利器,这里自己边学习边记录下过程.以下操作在Wi ...

最新文章

  1. 关于内存管理/set/get方法
  2. Linux图形界面与命令行模式切换
  3. asp.net MVC 中使用dataannotation验证Model
  4. [转载] java8 lambda表达式 List转为Map
  5. 真与开源“化敌为友”:微软连自己的 Linux 发行版都有了!
  6. Android子线程进度条不显示的问题
  7. js td innerHTML
  8. 苹果7pnfc功能门禁卡_苹果7plus怎么打开nfc这个功能
  9. 线段树,方差,数学(Variance,玲珑杯 Round#5 H lonlife 1063)
  10. pygraphviz win7安装报错解决
  11. 【Android实战】保存QQ账号与密码
  12. 外贸必备通讯工具之一,AntTone 的介绍及使用教程
  13. Spring Boot、Spring Cloud 自定义配置文件(如何整合配置中心)
  14. 星际战魂java_星际战甲 专精聚魂选择推荐
  15. 家庭最好用的投影仪,投影仪怎么选择?
  16. 微信小程序开发 01
  17. 【Crypto】RSA
  18. 搭建私服-docker registry
  19. 关于 VS Code 下载安装及汉化的方法
  20. Qt 3D:高级自定义材质QML示例

热门文章

  1. 微服务化之前需要先无状态化和容器化
  2. 适合小团队协作、任务管理和进度跟踪的项目管理工具
  3. 车 局部路径规划与避障
  4. Java枚举类使用方式
  5. find与findb
  6. HTML的标签描述6
  7. pandas object转float_数据分析篇 | Pandas基础用法6【完结篇】
  8. 条件选择结构:星期计划(switch)
  9. hbase 默认目录_HBase 配置示例
  10. RAFT-3D: 基于刚体变换的场景流估计(ICCV 2021)