python 之路,致那些年,我们依然没搞明白的编码

本节内容

  编码回顾

  编码转换

  Python的bytes类型

编码回顾

在备编码相关的课件时,在知乎上看到一段关于Python编码的回答

这哥们的这段话说的太对了,搞Python不把编码彻底搞明白,总有一天它会猝不及防坑你一把。

不过感觉这哥们的答案并没把编码问题写明白,所以只好亲自动笔了。 

折腾编码问题,有很多次,我以为自已明白了,最终发现,那只不过是自圆其说而已,这一次,终于100%确定,动笔即不再改!

看这篇文章前,你应该已经知道了为什么有编码,以及编码的种类情况

  • ASCII 占1个字节,只支持英文
  • GB2312 占2个字节,支持6700+汉字
  • GBK GB2312的升级版,支持21000+汉字
  • Shift-JIS 日本字符
  • ks_c_5601-1987 韩国编码
  • TIS-620 泰国编码

由于每个国家都有自己的字符,所以其对应关系也涵盖了自己国家的字符,但是以上编码都存在局限性,即:仅涵盖本国字符,无其他国家字符的对应关系。应运而生出现了万国码,他涵盖了全球所有的文字和二进制的对应关系,

  • Unicode 2-4字节 已经收录136690个字符,并还在一直不断扩张中...

Unicode 起到了2个作用:

  1. 直接支持全球所有语言,每个国家都可以不用再使用自己之前的旧编码了,用unicode就可以了。(就跟英语是全球统一语言一样)
  2. unicode包含了跟全球所有国家编码的映射关系,为什么呢?后面再讲

Unicode解决了字符和二进制的对应关系,但是使用unicode表示一个字符,太浪费空间。例如:利用unicode表示“Python”需要12个字节才能表示,比原来ASCII表示增加了1倍。

由于计算机的内存比较大,并且字符串在内容中表示时也不会特别大,所以内容可以使用unicode来处理,但是存储和网络传输时一般数据都会非常多,那么增加1倍将是无法容忍的!!!

为了解决存储和网络传输的问题,出现了Unicode Transformation Format,学术名UTF,即:对unicode中的进行转换,以便于在存储和网络传输时可以节省空间!

  • UTF-8: 使用1、2、3、4个字节表示所有字符;优先使用1个字符、无法满足则使增加一个字节,最多4个字节。英文占1个字节、欧洲语系占2个、东亚占3个,其它及特殊字符占4个
  • UTF-16: 使用2、4个字节表示所有字符;优先使用2个字节,否则使用4个字节表示。
  • UTF-32: 使用4个字节表示所有字符;

总结:UTF 是为unicode编码 设计 的一种 在存储 和传输时节省空间的编码方案。

字符在硬盘上的存储 

无论以什么编码在内存里显示字符,存到硬盘上都是2进制。

Highlighter">
rush:python;gutter:true;">    ascii编码(美国):
        l   0b1101100
        o   0b1101111
        v   0b1110110
        e   0b1100101
    GBK编码(中国):
        老   0b11000000 0b11001111
        男   0b11000100 0b11010000
        孩   0b10111010 0b10100010
Shift_JIS编码(日本):
    私   0b10001110 0b10000100
    は   0b10000010 0b11001101

ks_c_5601-1987编码(韩国):
    나   0b10110011 0b10101010
    는   0b10110100 0b11000010

TIS-620编码(泰国):
    ฉัน  0b10101001 0b11010001 0b10111001
...
rush:python;gutter:true;">要注意的是,存到硬盘上时是以何种编码存的,再从硬盘上读出来时,就必须以何种编码读,要不然就乱了。。

  

编码的转换 

虽然国际语言是英语 ,但大家在自己的国家依然说自已的语言,不过出了国, 你就得会英语 编码也一样,虽然有了unicode and utf-8 , 但是由于历史问题,各个国家依然在大量使用自己的编码,比如中国的windows,认编码依然是gbk,而不是utf-8

基于此,如果中国的软件出口到美国,在美国人的电脑上就会显示乱码,因为他们没有gbk编码。 若想让中国的软件可以正常的在 美国人的电脑上显示,只有以下2条路可走:

  1. 让美国人的电脑上都装上gbk编码
  2. 把你的软件编码以utf-8编码

第1种方法几乎不可能实现,第2种方法比较简单。 但是也只能是针对新开发的软件。 如果你之前开发的软件就是以gbk编码的,上百万行代码可能已经写出去了,重新编码成utf-8格式也会费很大力气。

so,针对已经用gbk开发完毕的项目,以上2种方案都不能轻松的让项目在美国人电脑上正常显示,难道没有别的办法了么? 有, 还记得我们讲unicode其中一个功能是其包含了跟全球所有国家编码的映射关系,意思就是,你写的是gbk的“路飞学城”,但是unicode能自动知道它在unicode中的“路飞学城”的编码是什么,如果这样的话,那是不是意味着,无论你以什么编码存储的数据 ,只要你的软件在把数据从硬盘读到内存里,转成unicode来显示,就可以了。 由于所有的系统、编程语言都支持unicode,那你的gbk软件放到美国电脑 上,加载到内存里,变成了unicode,中文就可以正常展示啦。

 这个表你自己也可以下载下来 

unicode与gbk的映射表 http://www.unicode.org/charts/ 

python3的执行过程

在看实际代码的例子前,我们来聊聊,python3 执行代码的过程

  1. 解释器找到代码文件,把代码字符串按文件头定义的编码加载到内存,转成unicode
  2. 代码字符串按照语法规则进行解释,
  3. 所有的变量字符都会以unicode编码声明

编码转换过程

实际代码演示,在py3上 把你的代码以utf-8编写, 保存,然后在windows上执行,

Highlighter">
rush:python;gutter:true;">s = '路飞学城'
print(s)

so,一切都很美好,到这里,我们关于编码的学习按说就可以结束了。

但是,如生活一样,美好的表面下,总是隐藏着不尽如人意,上面的utf-8编码之所以能在windows gbk的终端下显示正常,是因为到了内存里python解释器把utf-8转成了unicode,但是这只是python3,并不是所有的编程语言在内存里认编码都是unicode,比如 万恶的python2 就不是, 它的认编码是ASCII,想写中文,就必须声明文件头的coding为gbk or utf-8,声明之后,python2解释器仅以文件头声明的编码去解释你的代码,加载到内存后,并不会主动帮你转为unicode,也就是说,你的文件编码是utf-8,加载到内存里,你的变量字符串就也是utf-8,这意味着什么你知道么?。。。意味着,你以utf-8编码的文件,在windows是乱码。 

乱是正常的,不乱才不正常,因为只有2种情况 ,你的windows上显示才不会乱

  1. 字符串以GBK格式显示
  2. 字符串是unicode编码

既然Python2并不会自动的把文件编码转为unicode存在内存里, 那就只能使出最后一招了,你自己人肉转。Py3 自动文件编码转为unicode必定是调用了什么方法,这个方法就是,decode(解码) 和encode(编码)

Highlighter">
 decode 解码 --> Unicode 
    Unicode --> encode 编码 --> GBK / UTF-8 .. 

decode示例

encode 示例

记住下图规则

 

如何验证编码转对了呢?

1. 查看数据类型,python 2 里有专门的unicode 类型 2. 查看unicode编码映射表

  unicode字符是有专门的unicode类型来判断的,但是utf-8,gbk编码的字符都是str,你如果分辨出来的当前的字符串数据是何种编码的呢? 有人说可以通过字节长度判断,因为utf-8一个中文占3字节,gbk一个占2字节

靠上面字节个数,虽然也能大体判断是什么类型,但总觉得不是很专业。

怎么才能精确的验证一个字符的编码呢,就是拿这些16进制的数跟编码表里去匹配。

“路飞学城”的unicode编码的映射位置是 u'\u8def\u98de\u5b66\u57ce' ,‘\u8def’ 就是‘路’,到表里搜一下。 “路飞学城”对应的GBK编码是'\xc2\xb7\xb7\xc9\xd1\xa7\xb3\xc7' ,2个字节一个中文,"路" 的二进制 "\xc2\xb7"是4个16进制,正好2字节,拿它到unicode映射表里对一下, 发现是G0-4237,并不是\xc2\xb7呀。。。擦。演砸了吧。。

 

再查下“飞” \u98de ,对应的是G0-3749, 跟\xb7\xc9也对不上。

虽然对不上, 但好\xc2\xb7 和G0-4237中的第2位的2和第4位的7对上了,“飞”字也是一样,莫非巧合? 

把他们都转成2进制显示试试 

Highlighter">
rush:python;gutter:true;">     路
        C               2
        8   4   2   1   8   4   2   1
        1   1   0   0   0   0   1   0
    B               7
    8   4   2   1   8   4   2   1
    <strong>1   0   1   1   0   1   1   1</strong>


    飞
    B               7
    8   4   2   1   8   4   2   1
    1   0   1   1   0   1   1   1

    C               9
    8   4   2   1   8   4   2   1
    1   1   0   0   1   0   0   1

这个“路”还是跟G0-4237对不上呀,没错, 但如果你把路\xc2\xb7的每个二进制字节的左边第一个bit变成0试试呢, 我擦,加起来就真的是4237了呀。。难道又是巧合???  

必然不是,是因为,GBK的编码表示形式决定的。。因为GBK编码在设计初期就考虑到了要兼容ASCII,即如果是英文,就用一个字节表示,2个字节就是中文,但如何区别连在一起的2个字节是代表2个英文字母,还是一个中文汉字呢? 中国人如此聪明,决定,2个字节连在一起,如果每个字节的第1位(也就是相当于128的那个2进制位)如果是1,就代表这是个中文,这个首位是128的字节被称为高字节。 也就是2个高字节连在一起,必然就是一个中文。 你怎么如此笃定?因为0-127已经表示了英文的绝大部分字符,128-255是ASCII的扩展表,表示的都是极特殊的字符,一般没什么用。所以中国人就直接拿来用了。 

问:那为什么上面 "\xc2\xb7"的2进制要把128所在的位去掉才能与unicode编码表里的G0-4237匹配上呢?

这只能说是unicode在映射表的表达上直接忽略了高字节,但真正映射的时候 ,肯定还是需要用高字节的哈。

Python bytes类型

在python 2 上写字符串

Highlighter">
>> s = "路飞"
>>> print s
路飞
>>> s
'\xe8\xb7\xaf\xe9\xa3\x9e'

虽说打印的是路飞,但直接调用变量s,看到的却是一个个的16进制表示的二进制字节,我们怎么称呼这样的数据呢?直接叫二进制么?也可以, 但相比于010101,这个数据串在表示形式上又把2进制转成了16进制来表示,这是为什么呢? 哈,为的就是让人们看起来更可读。我们称之为bytes类型,即字节类型, 它把8个二进制一组称为一个byte,用16进制来表示。  

说这个有什么意思呢?

想告诉你一个事实, 就是,python2的字符串其实更应该称为字节串。 通过存储方式就能看出来, 但python2里还有一个类型是bytes呀,难道又叫bytes又叫字符串? 嗯 ,是的,在python2里,bytes == str , 其实就是一回事 

除此之外呢, python2里还有个单独的类型是unicode,把字符串解码后,就会变成unicode

Highlighter">
>> s
'\xe8\xb7\xaf\xe9\xa3\x9e' #utf-8
>>> s.decode('utf-8')
u'\u8def\u98de' #unicode 在unicode编码表里对应的位置 
>>> print(s.decode('utf-8')) 
路飞 #unicode 格式的字符

由于Python创始人在开发初期认知的局限性,其并未预料到python能发展成一个全球流行的语言,导致其开发初期并没有把支持全球各国语言当做重要的事情来做,所以就轻佻的把ASCII当做了认编码。 当后来大家对支持汉字、日文、法语等语言的呼声越来越高时,Python于是准备引入unicode,但若直接把认编码改成unicode的话是不现实的, 因为很多软件就是基于之前的认编码ASCII开发的,编码一换,那些软件的编码就都乱了。所以Python 2 就直接 搞了一个新的字符类型,就叫unicode类型,比如你想让你的中文在全球所有电脑上正常显示,在内存里就得把字符串存成unicode类型

Highlighter">
>> s = "路飞"
>>> s
'\xe8\xb7\xaf\xe9\xa3\x9e'
>>> s2 = s.decode("utf-8")
>>> s2
u'\u8def\u98de'
>>> type(s2)

时间来到2008年,python发展已近20年,创始人龟叔越来越觉得python里的好多东西已发展的不像他的初衷那样,开始变得臃肿、不简洁、且有些设计让人摸不到头脑,比如unicode 与str类型,str 与bytes类型的关系,这给很多python程序员造成了困扰。 龟叔再也忍不了,像之前一样的修修补补已不能让Python变的更好,于是来了个大变革,python3横空出世,不兼容python2,python3比python2做了非常多的改进,其中一个就是终于把字符串变成了unicode,文件认编码变成了utf-8,这意味着,只要用python3,无论你的程序是以哪种编码开发的,都可以在全球各国电脑上正常显示,真是太棒啦!

PY3 除了把字符串的编码改成了unicode,还把str 和bytes 做了明确区分, str 就是unicode格式的字符, bytes就是单纯二进制啦。

最后一个问题,为什么在py3里,把unicode编码后,字符串就变成了bytes格式? 你直接给我直接打印成gbk的字符展示不好么?我想其实py3的设计真是煞费苦心,就是想通过这样的方式明确的告诉你,想在py3里看字符,必须得是unicode编码,其它编码一律按bytes格式展示。 

好吧,就说这么多吧。 

最后再提示一下,Python只要出现各种编码问题,无非是哪里的编码设置出错了常见编码错误的原因有:

    • Python解释器的认编码
    • Python源文件文件编码
    • Terminal使用的编码
    • 操作系统的语言设置

掌握了编码之前的关系后,挨个排错就好啦  

  

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


我最近重新拾起了计算机视觉,借助Python的opencv还有face_recognition库写了个简单的图像识别demo,额外定制了一些内容,原本想打包成exe然后发给朋友,不过在这当中遇到了许多小问题,都解决了,记录一下踩过的坑。 1、Pyinstaller打包过程当中出现warning,跟d
说到Pooling,相信学习过CNN的朋友们都不会感到陌生。Pooling在中文当中的意思是“池化”,在神经网络当中非常常见,通常用的比较多的一种是Max Pooling,具体操作如下图: 结合图像理解,相信你也会大概明白其中的本意。不过Pooling并不是只可以选取2x2的窗口大小,即便是3x3,
记得大一学Python的时候,有一个题目是判断一个数是否是复数。当时觉得比较复杂不好写,就琢磨了一个偷懒的好办法,用异常处理的手段便可以大大程度帮助你简短代码(偷懒)。以下是判断整数和复数的两段小代码: 相信看到这里,你也有所顿悟,能拓展出更多有意思的方法~
文章目录 3 直方图Histogramplot1. 基本直方图的绘制 Basic histogram2. 数据分布与密度信息显示 Control rug and density on seaborn histogram3. 带箱形图的直方图 Histogram with a boxplot on t
文章目录 5 小提琴图Violinplot1. 基础小提琴图绘制 Basic violinplot2. 小提琴图样式自定义 Custom seaborn violinplot3. 小提琴图颜色自定义 Control color of seaborn violinplot4. 分组小提琴图 Group
文章目录 4 核密度图Densityplot1. 基础核密度图绘制 Basic density plot2. 核密度图的区间控制 Control bandwidth of density plot3. 多个变量的核密度图绘制 Density plot of several variables4. 边
首先 import tensorflow as tf tf.argmax(tenso,n)函数会返回tensor中参数指定的维度中的最大值的索引或者向量。当tensor为矩阵返回向量,tensor为向量返回索引号。其中n表示具体参数的维度。 以实际例子为说明: import tensorflow a
seaborn学习笔记章节 seaborn是一个基于matplotlib的Python数据可视化库。seaborn是matplotlib的高级封装,可以绘制有吸引力且信息丰富的统计图形。相对于matplotlib,seaborn语法更简洁,两者关系类似于numpy和pandas之间的关系,seabo
Python ConfigParser教程显示了如何使用ConfigParser在Python中使用配置文件。 文章目录 1 介绍1.1 Python ConfigParser读取文件1.2 Python ConfigParser中的节1.3 Python ConfigParser从字符串中读取数据
1. 处理Excel 电子表格笔记(第12章)(代码下载) 本文主要介绍openpyxl 的2.5.12版处理excel电子表格,原书是2.1.4 版,OpenPyXL 团队会经常发布新版本。不过不用担心,新版本应该在相当长的时间内向后兼容。如果你有新版本,想看看它提供了什么新功能,可以查看Open
1. 发送电子邮件和短信笔记(第16章)(代码下载) 1.1 发送电子邮件 简单邮件传输协议(SMTP)是用于发送电子邮件的协议。SMTP 规定电子邮件应该如何格式化、加密、在邮件服务器之间传递,以及在你点击发送后,计算机要处理的所有其他细节。。但是,你并不需要知道这些技术细节,因为Python 的
文章目录 12 绘图实例(4) Drawing example(4)1. Scatterplot with varying point sizes and hues(relplot)2. Scatterplot with categorical variables(swarmplot)3. Scat
文章目录 10 绘图实例(2) Drawing example(2)1. Grouped violinplots with split violins(violinplot)2. Annotated heatmaps(heatmap)3. Hexbin plot with marginal dist
文章目录 9 绘图实例(1) Drawing example(1)1. Anscombe’s quartet(lmplot)2. Color palette choices(barplot)3. Different cubehelix palettes(kdeplot)4. Distribution
Python装饰器教程展示了如何在Python中使用装饰器基本功能。 文章目录 1 使用教程1.1 Python装饰器简单示例1.2 带@符号的Python装饰器1.3 用参数修饰函数1.4 Python装饰器修改数据1.5 Python多层装饰器1.6 Python装饰器计时示例 2 参考 1 使
1. 用GUI 自动化控制键盘和鼠标第18章 (代码下载) pyautogui模块可以向Windows、OS X 和Linux 发送虚拟按键和鼠标点击。根据使用的操作系统,在安装pyautogui之前,可能需要安装一些其他模块。 Windows: 不需要安装其他模块。OS X: sudo pip3
文章目录 生成文件目录结构多图合并找出文件夹中相似图像 生成文件目录结构 生成文件夹或文件的目录结构,并保存结果。可选是否滤除目录,特定文件以及可以设定最大查找文件结构深度。效果如下: root:[z:/] |--a.py |--image | |--cat1.jpg | |--cat2.jpg |
文章目录 VENN DIAGRAM(维恩图)1. 具有2个分组的基本的维恩图 Venn diagram with 2 groups2. 具有3个组的基本维恩图 Venn diagram with 3 groups3. 自定义维恩图 Custom Venn diagram4. 精致的维恩图 Elabo
mxnet60分钟入门Gluon教程代码下载,适合做过深度学习的人使用。入门教程地址: https://beta.mxnet.io/guide/getting-started/crash-course/index.html mxnet安装方法:pip install mxnet 1 在mxnet中使
文章目录 1 安装2 快速入门2.1 基本用法2.2 输出图像格式2.3 图像style设置2.4 属性2.5 子图和聚类 3 实例4 如何进一步使用python graphviz Graphviz是一款能够自动排版的流程图绘图软件。python graphviz则是graphviz的python实