吐血总结,彻底明白 python3 编码原理

写的不错,转发学习一下,侵删。。

原文地址https://zhuanlan.zhihu.com/p/40834093

防止原文看不到了 这里粘贴复制一下:::)

关于编码的历史演变,utf-8是如何一步步发展来的,windows为啥依旧保持gbk的编码。。。
等等这些问题,网上一搜一大堆,大部分都是转发、分享后的雷同内容,依旧解决不了我内心的疑惑。。。
编码是个蛋疼的事情,倘若不弄清楚, 怎么在中国混?
经过自己查阅多方文档、多次深入实验,我树立了对编码的基本世界观。

基础内容请自行谷歌..废话不多说,直接上干货!!

下面用几个简单的代码段, 一步步讲解编码中“编”和“解”的问题!!(linux中运行)


“ 代码 一 ”:

import sys, locale

s = "小甲"
print(s)
print(type(s))
print(sys.getdefaultencoding())
print(locale.getdefaultlocale()) with open("utf1","w",encoding = "utf-8") as f:
f.write(s)
with open("gbk1","w",encoding = "gbk") as f:
f.write(s)
with open("jis1","w",encoding = "shift-jis") as f:
f.write(s)

代码很简单,学过Python的人应该都能看懂是啥意思~~
我们看一下运行结果:

“ 代码 一 ” 运行结果:

小甲
<class 'str'>
utf-8
('en_US', 'UTF-8')

正如大家所想, 就是将“小甲”原样打印出来, 再把“小甲”存到3个文件中。
(shift-jis是日文编码格式)

这么的代码我还会不知道吗? ( 重点在后面 )

这里解释一下打印出来的两个“utf-8”是什么意思:

上面的 utf-8 指:系统默认编码

  • 注: 不要把系统以为是操作系统,这里可以理解成python3的编译器本身

下面的 utf-8 指:本地默认编码

  • 注: 这个才是操作系统的编码。(在Windows运行会变成gbk)

现在我们分别查看utf1 、gbk1、jis1 这三个文件的内容:

utf1 : 小甲
gbk1 : С▒▒▒
jis1 : ▒▒▒b

问题:
为什么 utf1 的内容很清楚,没有编码问题,而gbk1 、jis1 的内容都出现了乱码?

解释:
因为我文件存储时用的编码格式不是utf-8,而此时读取这两个文件时,使用的是linux操作系统的默认编码“utf-8”。
那么写入磁盘时不是用utf-8, 读出时却用utf-8,当然读不出来了。
(这里需要大家了解encoding的真实作用)


“ 代码 二 ”:

#coding=gbk
import sys, locale s = "小甲"
#coding=gbk
import sys, locale s = "小甲"
print(s)
print(type(s))
print(sys.getdefaultencoding())
print(locale.getdefaultlocale()) with open("utf2","w",encoding = "utf-8") as f:
f.write(s)
with open("gbk2","w",encoding = "gbk") as f:
f.write(s)
with open("jis2","w",encoding = "shift-jis") as f:
f.write(s)

代码结结构一样很简单
但是请大家注意: 我在头部加了某个编码声明
在代码运行前, 请大家自行猜测结果~~~

“ 代码 二 ” 运行结果 :

灏忕敳
<class 'str'>
utf-8
('en_US', 'UTF-8')
Traceback (most recent call last):
File "2", line 15, in <module>
f.write(s)
UnicodeEncodeError: 'shift_jis' codec can't encode character '\u704f' in position 0: illegal multibyte sequence

问题来了:

1、代码中明明 s = “小甲”, 为什么变成了 “灏忕敳” ??
2、为什么 jis 的编码失败了?(之前顶多只出现了乱码的问题,还不会报错,那它内部到底发生了什么?)
3、“coding=gbk” 到底是什么意思??
4、我明明写了 “coding=gbk” 的编码声明,为什么系统编码、本地默认编码还是没有改变?(那我写了有啥用?)

解释一下:

以上这么多问题, 主要是因为没搞清楚头文件的 “coding=gbk” 编码声明是什么意思!!

1、它的意思是python3编译器在读取该.py文件时候,我应该用什么格式将它 “解码”?只和读取有关,所以当你确定你代码编辑时候用的是什么格式编码的,你才能把相应的编码格式写入头文件。
(在此示范代码中,我用的是linux的默认编码编辑,也就是utf-8,那么在后面运行的时候,却要求解释器用gbk去解码,自然很过分,就会出现了s=“小甲” 乱码的问题)

(大家一定要知道,编码是 “编” 和 “解” 的两个步骤,一定要一一对应才能正确解码!虽然通常我们都叫“编码格式”,这是有一定误导性的。
实际上另一半是“解码格式”,要有意识地区分 “编” 和 “解” ,我们不能像网上有些文章一样将这两者混为一谈!!)

2、根据上面的解释应该可以明白,写了它之后,并不会更改本地、系统默认编码。
(本地默认编码只跟操作系统相关,linux中是utf-8,windows中是gbk。)
(系统默认编码实际是有python3和python2的差异的,python3是utf-8,python2是ascii。)

3、那么,上面两种编码的作用体现在哪里呢?

敲黑板,划重点:

系统默认编码 指:
在python3编译器读取.py文件时,若没有头文件编码声明,则默认使用“utf-8”来对.py文件进行解码。并且在调用 encode()这个函数时,不传参的话默认是“ utf-8 ”。(这与下面的open( )函数中的“encoding”参数要做区分,非常误导人!!!)

本地默认编码 指:
在你编写的python3程序时,若使用了 open( )函数 ,而不给它传入 “ encoding ” 这个参数,那么会自动使用本地默认编码。没错,如果在Windows系统中,就是默认用gbk格式!!!

(这个问题困扰了我好久, 不说好了一直默认utf-8到天长地久的嘛,咋换成win后就频频失信呢。所以请大家在这里注意:linux中可以不用传“ encoding” 的参数, 而win中不能忘了~~~)

4、再来回答一下报错的问题:
因为我们的编译器已经用了gbk来解码此.py文件了,所以读取出来的变量 s 已经变成了我们现在看到的“ 灏忕敳 ” 了!那么此时把 s 存到磁盘文件中,实际上存的是乱码后的 “ 灏忕敳 ”。而在日文中,是没有这3个字的, 所以自然反馈说 “ 在 position 0 的位置,编码失败了”

现在我们再来分别查看utf2 、gbk2、jis2 这三个文件的内容:

utf2 : 灏忕敳
gbk2 : 小甲
jis2 :

(跟你想象中的结果是否一样呢??嘿嘿嘿~~)

问题:

1、为什么 我用 “utf-8 ” 去编码存储,后来用linux默认的 “ utf-8 ” 去解码,却出现乱码?
2、为什么我用“ gbk ” 去编码存储, 后面用linux默认的 “ utf-8 ” 去解码,明明编码、解码格式不一致,却能够正常显示?

解释:

1、实际上面两个问题是同一个问题,相信细心的同学已经知道问题出在哪里了,我上文已经说的很清楚了。此时的变量 s 已经变成了“ 灏忕敳 ”, 那么utf2这个文本文件自然是显示“灏忕敳”。

2、而“灏忕敳”这三个字符是怎么来的呢?

第1步:  小甲(unicode)   ---用 "utf-8" 编码--->    e5b0 8fe7 94b2 (utf-8编码后的二进制代码)

第2步:  e5b0 8fe7 94b2   ---用 “gbk” 解码--->     " 灏忕敳 " (unicode)(乱码)

第3步:  “ 灏忕敳 ”     --- 用 “ gbk ” 编码--->     e5b0 8fe7 94b2 ( 第2步的逆向)

第4步:  e5b0 8fe7 94b2     ---用 “ utf-8 ” 解码--->    小甲(unicode)

我想上述的步骤够清楚了吧 ~ 
第3、 4 步就是逆推回去,就变成了正常的 “ 小甲 ”
看懂了这个 “ 编码 ” 和 “ 解码 ” 的过程,你的编码问题已经解决大半了!


“ 代码 三 ”:

#coding=shift-jis
import sys, locale s = "小甲"
print(s)
print(type(s))
print(sys.getdefaultencoding())
print(locale.getdefaultlocale(), "\n\n") a = s.encode("shift-jis")
print(a)
print(type(a))
b = a.decode("utf-8")
print(b)
print(type(b))
print(a.decode("gbk")) with open("utf3","w",encoding = "utf-8") as f:
f.write(s)
with open("gbk3","w",encoding = "gbk") as f:
f.write(s)
with open("jis3","w",encoding = "shift-jis") as f:
f.write(s)

代码整体结构还是老样子,只不过中间多加了一小段代码,便于解释~

“ 代码 三 ” 运行结果 :

蟆冗抜
<class 'str'>
utf-8
('en_US', 'UTF-8') b'\xe5\xb0\x8f\xe7\x94\xb2'
<class 'bytes'>
小甲
<class 'str'>
灏忕敳

这里可以看到,此时我们的变量 s 已经变成了“ 蟆冗抜 ”(另一个用jis解码造成的乱码)。

那么此时,我把 “ 蟆冗抜 ” 用 “ shift-jis ” 解码回去并赋值给变量 a,打印一下,可以看到 a 就是正常显示的 “ 小甲 ”, 这也证明了我上面的推断是绝对正确的!!

现在,我们依旧分别查看一下 utf3 、gbk3、jis3 这三个文件的内容:

utf3 : 蟆冗抜
gbk3 : ▒▒ߒi
jis3 : 小甲

(oops~~ 见鬼,又是这么乱七八糟的东西)

这里我澄清一下,实际上utf3这个至少还能有文字,这叫乱码。而gbk3那个东西一团黑是什么鬼,是报错,linux的默认编码无法解码gbk3的文件,所以打印地乱七八糟。

问题:

  • 为什么 utf3 的文件是显示乱码, 而 gbk3 的文件却是报错呢??

解释:

  • 这是因为 utf-8 与 gbk 编码的算法差异。
  • 我们最常看到的是utf-8解码报错,因为它是可变长的的编码,有1个字节的英文字符,也有2个字节的阿拉伯文,也有3个字节的中文和日文。
  • gbk对英文是使用单字节编码(也就意味着兼容ascii),而gbk对中文部分是采取定长的2字节,总体编码范围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间。所以说它只要没有碰到尾字节在40之内的字符,都会一股脑地按照2字节去解码成中文。而中文在 utf-8 编码后,一般是三字节的。当解码的字节数和编码的字节数不匹配时,自然会造成全是乱码的局面。

(此处感谢 “流放的国王” 的指正~~)

  • 而utf-8是有严格定义的,一个字节的字符高位必须是0;三个字节的字符中,第一个字节的高位是1110开头。
  • 相关utf-8的编码算法链接

至此,代码的示范部分就结束了~~ 码字码得我手酸 ~~~~(>_<)~~~~


最后,

tips:

1、所有文件的编码格式都由你当下使用的编辑器决定的!!在windows中编辑的文本放在浏览器解析显示的时候,有时乱码,有时又正常,这是由于windows中很多文本编辑器默认使用和操作系统一致的编码格式。
所以在文本存储前,一定要搞清楚我们用的是utf-8还是gbk!!!
而当你使用Python的 open( ) 函数时,是内存中的进程与磁盘的交互,而这个交互过程中的编码格式则是使用操作系统的默认编码(Linux为utf-8,windows为gbk)

2、相信学Python的同学们经常会听到,python3 的默认编码是utf-8。而有的时候,又有人说python3 的默认编码是unicode,那么是不是会有人跟我初学时候一样傻傻分不清楚这两者的关系呢?

  • 实际上unicode就是一个字符集,一个字符与数字一一对应的映射关系,因为它一律以2个字节编码(或者也有4个字节的,这里不讨论),所以占用空间会大一些,一般只用于内存中的编码使用。
  • 而 utf-8 是为了实现unicode 的传输和存储的。因为它可变长,存英文时候可以节省大量存储空间。传输时候也节省流量,所以更加 “ international ”~

所以说,上述两种说法没有歧义,进程在内存中的表现是“ unicode ”的编码;当python3编译器读取磁盘上的.py文件时,是默认使用“utf-8”的;当进程中出现open(), write() 这样的存储代码时,需要与磁盘进行存储交互时,则是默认使用操作系统的默认编码。

再次申明:原文地址https://zhuanlan.zhihu.com/p/40834093 这里复制只为学习记录一下 侵删

转发:吐血总结,彻底明白 python3 编码原理的更多相关文章

  1. 【python测试开发栈】带你彻底搞明白python3编码原理

    在之前的文章中,我们介绍过编码格式的发展史:[文章传送门-todo].今天我们通过几个例子,来彻底搞清楚python3中的编码格式原理,这样你之后写python脚本时碰到编码问题,才能有章可循. 我们 ...

  2. python3编码问题

    继续收集python3编码问题相关资料 资料来源  鹏程的新浪博客(转载)http://blog.sina.com.cn/s/blog_6d7cf9e50102vo90.html  这篇鹏程老师写的关 ...

  3. 一篇文章彻底弄懂Base64编码原理

    在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. Base64的由来 目前Base64已经成为网 ...

  4. 知识扩展——(转)一篇文章彻底弄懂Base64编码原理

    在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. 一.Base64的由来 目前Base64已经成 ...

  5. [转帖]Nginx为什么高效?一文搞明白Nginx核心原理

    Nginx为什么高效?一文搞明白Nginx核心原理 咔咔侃技术 2019-09-06 15:37:00 https://www.toutiao.com/a6733057587622707724/ Ng ...

  6. BASE64编码原理分析脚本实现及逆向案例

    在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理你又了解多少?今天小编带大家了解一下Base64编码原理分析脚本实现及逆向案例的相关内容.   01编码由来 数 ...

  7. Base64图片编码原理,base64图片工具介绍,图片在线转换Base64

    Base64图片编码原理,base64图片工具介绍,图片在线转换Base64 DataURI 允许在HTML文档中嵌入小文件,可以使用 img 标签或 CSS 嵌入转换后的 Base64 编码,减少  ...

  8. Spark MLlib特征处理:OneHotEncoder OneHot编码 ---原理及实战

    http://m.blog.csdn.net/wangpei1949/article/details/53140372 Spark MLlib特征处理:OneHotEncoder OneHot编码 - ...

  9. 无线通信中FEC 编码原理及评价

    转自:http://blog.csdn.net/wiznet2012/article/details/7492146 大家好,前面我们给大家介绍了无线通信中FEC编码原理(1)和(2),今天继续献上F ...

随机推荐

  1. SpringBoot常用注解总结

    在SpringBoot框架中,注解做为一种隐式配置,极大的简化了之前xml文件的配置方式.SpringBoot中包含许多种类的注解,这里对在SpingBoot项目中经常使用到的一些注解的进行大致的归纳 ...

  2. 冲刺Offer - 二叉树的深度

    https://www.nowcoder.net/practice/435fb86331474282a3499955f0a41e8b?tpId=13&tqId=11191&tPage= ...

  3. c++11中的线程、锁和条件变量

    void func(int i, double d, const string& s) { cout << i << ", " << d ...

  4. Lua迭代器

    在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素.迭代器需要保留上一次成功调用的状态和下一次成功调用的状态,可以通过闭包提供的机制来实现这个任务(闭包中的外部局部变量可以用 ...

  5. VC,VB程序button、图标样式美化

    此处的"美化"指的不是通过代码进行美化你的程序.关于想进一步优化自己的程序界面的,最好还是去了解下SkinSharp吧.本文提及的是利用第三方资源编辑软件在不更改程序不论什么框架和 ...

  6. Android上拉查看详情实现

    京东淘宝有那么一种效果就是,上拉能够查看宝贝的详情,这里我也实现了一个类似的效果,也能够移植到商业项目上:先看看简单的效果图 实现原理事实上是利用了ScrollView的滚动和view的touch事件 ...

  7. 通过java类文件识别JDK编译版本号

    类文件里第5,6.7,8四个字节是jDK版本信息.当中5,6为小版本:7,8为大版本. 大版本号号相应JDK版本号例如以下: JDK版本 7,8字节 JDK8 52(0x34) JDK7 51(0x3 ...

  8. 【C/C++多线程编程之五】pthread线程深入理解

    多线程编程之pthread线程深入理解       Pthread是 POSIX threads 的简称,是POSIX的线程标准.           前几篇博客已经能给你初步的多线程概念.在进一步学 ...

  9. Write a program that gives count of common characters presented in an array of strings..(or array of

    转自出处 Write a program that gives count of common characters presented in an array of strings..(or arr ...

  10. web container和spring container之间的关系

    1 tomcat是一个web container,它提供java web应用的运行环境,因为它实现了好多java ee规范,比如java Servlet.JSP等,也就是说,没有它,java web应 ...