字符编码

一 了解字符编码的知识储备

一 计算机基础知识node

 

二 文本编辑器存取文件的原理(nodepad++,pycharm,word)python

#一、打开编辑器就打开了启动了一个进程,是在内存中的,因此,用编辑器编写的内容也都是存放与内存中的,断电后数据丢失

#二、要想永久保存,须要点击保存按钮:编辑器把内存的数据刷到了硬盘上。

#三、在咱们编写一个py文件(没有执行),跟编写其余文件没有任何区别,都只是在编写一堆字符而已。

三 python解释器执行py文件的原理 ,例如python test.py程序员

复制代码
#第一阶段:python解释器启动,此时就至关于启动了一个文本编辑器

#第二阶段:python解释器至关于文本编辑器,去打开test.py文件,从硬盘上将test.py的文件内容读入到内存中(小复习:pyhon的解释性,决定了解释器只关心文件内容,不关心文件后缀名)

#第三阶段:python解释器解释执行刚刚加载到内存中test.py的代码( ps:在该阶段,即真正执行代码时,才会识别python的语法,执行文件内代码,当执行到name="egon"时,会开辟内存空间存放字符串"egon")
复制代码

四 总结python解释器与文件本编辑的异同编程

#一、相同点:python解释器是解释执行文件内容的,于是python解释器具有读py文件的功能,这一点与文本编辑器同样

#二、不一样点:文本编辑器将文件内容读入内存后,是为了显示或者编辑,根本不去理会python的语法,而python解释器将文件内容读入内存后,可不是为了给你瞅一眼python代码写的啥,而是为了执行python代码、会识别python语法。

二 字符编码介绍

一 什么是字符编码浏览器

复制代码
  计算机要想工做必须通电,即用‘电’驱使计算机干活,也就是说‘电’的特性决定了计算机的特性。电的特性即高低电平(人类从逻辑上将二进制数1对应高电平,二进制数0对应低电平),关于磁盘的磁特性也是一样的道理。结论:计算机只认识数字

  很明显,咱们平时在使用计算机时,用的都是人类能读懂的字符(用高级语言编程的结果也无非是在文件内写了一堆字符),如何能让计算机读懂人类的字符?

  必须通过一个过程:
  #字符--------(翻译过程)------->数字 

  #这个过程实际就是一个字符如何对应一个特定数字的标准,这个标准称之为字符编码
复制代码

二 如下两个场景下涉及到字符编码的问题:服务器

#一、一个python文件中的内容是由一堆字符组成的,存取均涉及到字符编码问题(python文件并未执行,前两个阶段均属于该范畴)

#二、python中的数据类型字符串是由一串字符组成的(python文件执行时,即第三个阶段)

三 字符编码的发展史与分类(了解)网络

计算机由美国人发明,最先的字符编码为ASCII,只规定了英文字母数字和一些特殊字符与数字的对应关系。最多只能用 8 位来表示(一个字节),即:2**8 = 256,因此,ASCII码最多只能表示 256 个符号编程语言

固然咱们编程语言都用英文没问题,ASCII够用,可是在处理数据时,不一样的国家有不一样的语言,日本人会在本身的程序中加入日文,中国人会加入中文。编辑器

而要表示中文,单拿一个字节表表示一个汉子,是不可能表达完的(连小学生都认识两千多个汉字),解决方法只有一个,就是一个字节用>8位2进制表明,位数越多,表明的变化就多,这样,就能够尽量多的表达出不通的汉字编码

因此中国人规定了本身的标准gb2312编码,规定了包含中文在内的字符->数字的对应关系。

日本人规定了本身的Shift_JIS编码

韩国人规定了本身的Euc-kr编码(另外,韩国人说,计算机是他们发明的,要求世界统一用韩国编码,但世界人民没有搭理他们)

 

这时候问题出现了,精通18国语言的小周同窗谦虚的用8国语言写了一篇文档,那么这篇文档,按照哪国的标准,都会出现乱码(由于此刻的各类标准都只是规定了本身国家的文字在内的字符跟数字的对应关系,若是单纯采用一种国家的编码格式,那么其他国家语言的文字在解析时就会出现乱码)

因此迫切须要一个世界的标准(能包含全世界的语言)因而unicode应运而生(韩国人表示不服,而后没有什么卵用)

ascii用1个字节(8位二进制)表明一个字符

unicode经常使用2个字节(16位二进制)表明一个字符,生僻字须要用4个字节

例:

字母x,用ascii表示是十进制的120,二进制0111 1000

汉字已经超出了ASCII编码的范围,用Unicode编码是十进制的20013,二进制的01001110 00101101

字母x,用unicode表示二进制0000 0000 0111 1000,因此unicode兼容ascii,也兼容万国,是世界的标准

 

这时候乱码问题消失了,全部的文档咱们都使用可是新问题出现了,若是咱们的文档通篇都是英文,你用unicode会比ascii耗费多一倍的空间,在存储和传输上十分的低效

本着节约的精神,又出现了把Unicode编码转化为“可变长编码”的UTF-8编码。UTF-8编码把一个Unicode字符根据不一样的数字大小编码成1-6个字节,经常使用的英文字母被编码成1个字节,汉字一般是3个字节,只有很生僻的字符才会被编码成4-6个字节。若是你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间:

字符 ASCII Unicode UTF-8
A 01000001 00000000 01000001 01000001
x 01001110 00101101 11100100 10111000 10101101

从上面的表格还能够发现,UTF-8编码有一个额外的好处,就是ASCII编码实际上能够被当作是UTF-8编码的一部分,因此,大量只支持ASCII编码的历史遗留软件能够在UTF-8编码下继续工做。

四 总结字符编码的发展可分为三个阶段(重要)

  !!!重点!!!

基于目前的现状,内存中的编码固定就是unicode,咱们惟一可变的就是硬盘的上对应的字符编码。
此时你可能会以为,那若是咱们之后开发软时统一都用unicode编码,那么不就都统一了吗,关于统一这一点你的思路是没错的,但咱们不可会使用unicode编码来编写程序的文件,由于在通篇都是英文的状况下,耗费的空间几乎会多出一倍,这样在软件读入内存或写入磁盘时,都会徒增IO次数,从而下降程序的执行效率。于是咱们之后在编写程序的文件时应该统一使用一个更为精准的字符编码utf-8(用1Bytes存英文,3Bytes存中文),再次强调,内存中的编码固定使用unicode。
一、在存入磁盘时,须要将unicode转成一种更为精准的格式,utf-8:全称Unicode Transformation Format,将数据量控制到最精简

二、在读入内存时,须要将utf-8转成unicode
因此咱们须要明确:内存中用unicode是为了兼容万国软件,即使是硬盘中有各国编码编写的软件,unicode也有相对应的映射关系,但在如今的开发中,程序员广泛使用utf-8编码了,估计在未来的某一天等全部老的软件都淘汰掉了状况下,就能够变成:内存utf-8<->硬盘utf-8的形式了。

三 字符编码应用之文件编辑器

3.1 文本编辑器之nodpad++

 

  !!!乱码分析!!! 

3.2 文本编辑器之pycharm

以utf-8格式打开(选择reload)

  pycharm中:reload与convert的区别

3.3 文本编辑器之python解释器

复制代码
文件test.py以gbk格式保存,内容为:

  x='林'

不管是

  python2 test.py

仍是

  python3 test.py

都会报错(由于python2默认ascii,python3默认utf-8)

除非在文件开头指定#coding:gbk
复制代码

3.4 总结

!!!总结很是重要的两点!!!

#一、保证不乱吗的核心法则就是,字符按照什么标准而编码的,就要按照什么标准解码,此处的标准指的就是字符编码

#二、在内存中写的全部字符,一视同仁,都是unicode编码,好比咱们打开编辑器,输入一个“你”,咱们并不能说“你”就是一个汉字,此时它仅仅只是一个符号,该符号可能不少国家都在使用,根据咱们使用的输入法不一样这个字的样式可能也不太同样。只有在咱们往硬盘保存或者基于网络传输时,才能肯定”你“究竟是一个汉字,仍是一个日本字,这就是unicode转换成其余编码格式的过程了

                  unicode----->encode-------->utf-8

                  utf-8-------->decode---------->unicode

#补充:

浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器

若是服务端encode的编码格式是utf-8, 客户端内存中收到的也是utf-8编码的结果。

 

四 字符编码应用之python

4.1 执行python程序的三个阶段

python test.py   (我再强调一遍,执行test.py的第一步,必定是先将文件内容读入到内存中)

test.py文件内容以gbk格式保存的,内容为:

阶段一:启动python解释器

阶段二:python解释器此时就是一个文本编辑器,负责打开文件test.py,即从硬盘中读取test.py的内容到内存中

此时,python解释器会读取test.py的第一行内容,#coding:utf-8,来决定以什么编码格式来读入内存,这一行就是来设定python解释器这个软件的编码使用的编码格式这个编码,

能够用sys.getdefaultencoding()查看,若是不在python文件指定头信息#-*-coding:utf-8-*-,那就使用默认的

python2中默认使用ascii,python3中默认使用utf-8 

 

改正:在test.py指定文件头,字符编码必定要为gbk,

#coding:gbk
你好啊

阶段三:读取已经加载到内存的代码(unicode编码格式),而后执行,执行过程当中可能会开辟新的内存空间,好比x="egon"

复制代码
内存的编码使用unicode,不表明内存中全都是unicode,

在程序执行以前,内存中确实都是unicode,好比从文件中读取了一行x="egon",其中的x,等号,引号,地位都同样,都是普通字符而已,都是以unicode的格式存放于内存中的

可是程序在执行过程当中,会申请内存(与程序代码所存在的内存是俩个空间)用来存放python的数据类型的值,而python的字符串类型又涉及到了字符的概念

好比x="egon",会被python解释器识别为字符串,会申请内存空间来存放字符串类型的值,至于该字符串类型的值被识别成何种编码存放,这就与python解释器的有关了,而python2与python3的字符串类型又有所不一样。 
复制代码

4.2 python2与python3字符串类型的区别

一 在python2中有两种字符串类型str和unicode

str类型

当python解释器执行到产生字符串的代码时(例如x='上'),会申请新的内存地址,而后将'上'编码成文件开头指定的编码格式

要想看x在内存中的真实格式,能够将其放入列表中再打印,而不要直接打印,由于直接print()会自动转换编码,这一点咱们稍后再说。

#coding:gbk
x='上'
y='下'
print([x,y]) #['\xc9\xcf', '\xcf\xc2']
#\x表明16进制,此处是c9cf总共4位16进制数,一个16进制四4个比特位,4个16进制数则是16个比特位,即2个Bytes,这就证实了按照gbk编码中文用2Bytes
print(type(x),type(y)) #(<type 'str'>, <type 'str'>)

理解字符编码的关键!!!

内存中的数据一般用16进制表示,2位16进制数据表明一个字节,如\xc9,表明两位16进制,一个字节

gbk存中文须要2个bytes,而存英文则须要1个bytes,它是如何作到的???!!!

gbk会在每一个bytes,即8位bit的第一个位做为标志位,标志位为1则表示是中文字符,若是标志位为0则表示为英文字符

x=‘你a好’
转成gbk格式二进制位
8bit+8bit+8bit+8bit+8bit=(1+7bit)+(1+7bit)+(0+7bit)+(1+7bit)+(1+7bit)

这样计算机按照从左往右的顺序读:

#连续读到前两个括号内的首位标志位均为1,则构成一个中午字符:你

#读到第三个括号的首位标志为0,则该8bit表明一个英文字符:a

#连续读到后两个括号内的首位标志位均为1,则构成一个中午字符:好

也就是说,每一个Bytes留给咱们用来存真正值的有效位数只有7位,而在unicode表中存放的只是这有效的7位,至于首位的标志位与具体的编码有关,即在unicode中表示gbk的方式为:

(7bit)+(7bit)+(7bit)+(7bit)+(7bit)

 

按照上图翻译的结果,咱们能够去unicode关于汉字的对应关系中去查:连接:https://pan.baidu.com/s/1dEV3RYp

 

能够看到“”上“”对应的gbk(G0表明的是gbk)编码就为494F,即咱们得出的结果,而上对应的unicode编码为4E0A,咱们能够将gbk-->decode-->unicode

#coding:gbk
x='上'.decode('gbk')
y='下'.decode('gbk')
print([x,y]) #[u'\u4e0a', u'\u4e0b']

unicode类型

当python解释器执行到产生字符串的代码时(例如s=u'林'),会申请新的内存地址,而后将'林'以unicode的格式存放到新的内存空间中,因此s只能encode,不能decode

#coding:gbk
x=u'上' #等同于 x='上'.decode('gbk')
y=u'下' #等同于 y='下'.decode('gbk')
print([x,y]) #[u'\u4e0a', u'\u4e0b']
print(type(x),type(y)) #(<type 'unicode'>, <type 'unicode'>)

打印到终端

对于print须要特别说明的是:

当程序执行时,好比

x='上' #gbk下,字符串存放为\xc9\xcf

print(x) #这一步是将x指向的那块新的内存空间(非代码所在的内存空间)中的内存,打印到终端,按理说应该是存的什么就打印什么,但打印\xc9\xcf,对一些不熟知python编码的程序员,立马就懵逼了,因此龟叔自做主张,在print(x)时,使用终端的编码格式,将内存中的\xc9\xcf转成字符显示,此时就须要终端编码必须为gbk,不然没法正常显示原内容:上

对于unicode格式的数据来讲,不管怎么打印,都不会乱码

unicode这么好,不会乱码,那python2为什么还那么别扭,搞一个str出来呢?python诞生之时,unicode并未像今天这样普及,很明显,好的东西你能看得见,龟叔早就看见了,龟叔在python3中将str直接存成unicode,咱们定义一个str,无需加u前缀,就是一个unicode,屌不屌?

 

二 在python3 中也有两种字符串类型str和bytes

str是unicode

复制代码
#coding:gbk
x='上' #当程序执行时,无需加u,'上'也会被以unicode形式保存新的内存空间中,

print(type(x)) #<class 'str'>

#x能够直接encode成任意编码格式
print(x.encode('gbk')) #b'\xc9\xcf'
print(type(x.encode('gbk'))) #<class 'bytes'>
复制代码

很重要的一点是:看到python3中x.encode('gbk') 的结果\xc9\xcf正是python2中的str类型的值,而在python3是bytes类型,在python2中则是str类型

因而我有一个大胆的推测:python2中的str类型就是python3的bytes类型,因而我查看python2的str()源码,发现