05-01 字符编码

02 字符编码

一、知识储备

1、三大核心硬件

https://www.cnblogs.com/yang1333/p/12379991.html

2、文本编辑器读取文件内存的流程

https://www.cnblogs.com/yang1333/p/12404248.html

3、python解释器解释执行文件的流程

https://www.cnblogs.com/yang1333/p/12404248.html

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

  • 相同之处:python解释器与文件本编辑1~2阶段,都是启动程序,把程序从硬盘读入内存。
  • 不同之处:
    • python解释器是把刚刚读入内存的代码解释执行,有了语法的概念。
    • 文件本编辑是把刚刚读入的代码打印显示到屏幕上。
    • 小节:python解释器与文件本编辑处理代码的方式不同。

二、字符编码介绍

1、什么是字符编码表?

  • 就是一张字符与数字对应的关系表。

👇字符编码表的发展史👇

1、阶段一:一家独大 ASCII

  • ASCII:只采用8位二进制数(1Bytes)对应一个英文字符。

  • 现代计算机起源于美国,所以最先考虑仅仅是让计算机识别英文字符,于是诞生了ASCII表

2、阶段二:诸侯割据、天下大乱 ASCII、GBK、Shift-JIS

  • GBK:采用8位二进制数(1Bytes)对应一个英文字符,采用16位二进制数(2Bytes)对应一个中文字符。

  • 为了让计算机能够识别中文和英文,中国人定制了GBK。

  • 为让计算机能够识别自己国家的字符外加英文字符,各个国家都制定了自己的字符编码表。此时,美国人用的计算机里使用字符编码标准是ASCII、中国人用的计算机里使用字符编码标准是GBK、日本人用的计算机里使用字符编码标准是Shift_JIS

  • 此时无论是存还是取由于采用的字符编码表一样,所以肯定不会出现乱码问题,但问题是在美国人用的计算机里只能输入英文字符,而在中国人用的计算机里只能输入中文字符和英文字符....,毫无疑问我们希望计算机允许我们输入万国字符均可识别、不乱码,而现阶段计算机采用的字符编码ASCII、GBK、Shift_JIS都无法识别万国字符,所以我们必须定制一个兼容万国字符的编码表

3、阶段三:分久必合 unicode

  • unicode:采用16位二进制数(2Bytes)对应一个字符(注意:2个字节无论是对英文还是中文都是对于1个字符),个别生僻字采用4Byets、8Bytes。

  • unicode于1990年开始研发,1994年正式公布。

  • 很多地方或老的系统、应用软件仍会采用各种各样传统的编码,这是历史遗留问题。此处需要强调:软件是存放于硬盘的,而运行软件是要将软件加载到内存的,面对硬盘中存放的各种传统编码的软件,想让我们的计算机能够将它们全都正常运行而不出现乱码,内存中必须有一种兼容万国的编码,并且该编码需要与其他编码有相对应的映射/转换关系,这就是unicode的第二大特点产生的缘由

  • 为什么说unicde是过渡版本?

    • 1、对应万国码
    • 2、最重要的是,也就是说,目前unicode主要是争对于老的版本的兼容性,老版本的硬盘中的编码格式慢慢没了,它就没有意义了,这个时候内存就可以使用utf-8了。

4、编码与解码概念(重点:对应下面编码解码的使用)

  • 编码:由字符转换成内存中的unicode 或 由其他编码转换成内存中的unicode。
  • 解码:由内存中的unicode转换成字符 或 由内存中的unicode转换成其他编码。
  • 注意:unicode是存在内存当中

5、utf-8的由来

  • utf-8:采用8位二进制数(1Bytes)对应一个英文字符,采用24位二进制数(3Bytes)对应一个中文字符。

  • 注意:如果保存到硬盘的是GBK格式二进制,当初用户输入的字符只能是中文或英文,同理如果保存到硬盘的是Shift_JIS格式二进制,当初用户输入的字符只能是日文或英文……如果我们输入的字符中包含多国字符,那么该如何处理?

  • 理论上是可以将内存中unicode格式的二进制直接存放于硬盘中的,但由于unicode固定使用两个字节来存储一个字符,如果多国字符中包含大量的英文字符时,使用unicode格式存放会额外占用一倍空间(英文字符其实只需要用一个字节存放即可),然而空间占用并不是最致命的问题,最致命地是当我们由内存写入硬盘时会额外耗费一倍的时间,所以将内存中的unicode二进制写入硬盘或者基于网络传输时必须将其转换成一种精简的格式,这种格式即utf-8(全称Unicode Transformation Format,即unicode的转换格式)

  • 那为何在内存中不直接使用utf-8呢?unicode更像是一个过渡版本,我们新开发的软件或文件存入硬盘都采用utf-8格式,等过去几十年,所有老编码的文件都淘汰掉之后,会出现一个令人开心的场景,即硬盘里放的都是utf-8格式,此时unicode便可以退出历史舞台,内存里也改用utf-8,天下重新归于统一

👉总结字符编码:👆

  • 字符编码对应字符关系:
    • ASCII:只采用1个字节对应一个英文字符
    • GBK:采用1个字节对应一个英文字符,采用2个字节对应一个中文字符。
    • unicode:采用2个字节对应一个字符,生僻字采用4个字节、8个字节。(注意:无论是英文还是中文,都是采用2个字符)
    • utf-8:采用1个字节对应一个英文字符,采用3个字节对应一个中文字符。
  • 目前内存使用的编码: unicode
    • 注意:unicode兼容万国码,与万国字符都有对应关系。
    • unicode有2个作用:1、兼容万国字符 2、兼容万国字符编码表对应关系(unicode妥协于此,也就是它目前存在的主要作用)
  • 目前硬盘中使用的编码:utf-8、GBK、Shift-JIS
  • 内存固定使用unicode,我们可以改变的是存入硬盘采用的格式
    • 英文+汉字(文本编辑器) ---》 unicode(内存) ---》GBK(硬盘)
    • 英文+日文(文本编辑器) ---》 unicode(内存) ---》Shift-JIS(硬盘)
    • 万国字符(文本编辑器) ---》 unicode(内存) ---》utf-8(硬盘)
  • 注意:字符编码之间不可以进行转换。例如:GBK不能转成ASCII,因为这两种字符表的对应关系不一样。但是字符编码之间可以使用unicode作为中间介质,可以进行读写操作。

三、字符编码的应用

1、我们学习字符编码就是为了存取字符时不发生乱码问题:

# 1、内存中固定使用unicode无论输入任何字符都不会发生乱码

# 2、我们能够修改的是存/取硬盘的编码方式,如果编码设置不正确将会出现乱码问题。乱码问题分为两种:存乱了,读乱了

# 2.1 存乱了:如果用户输入的内容中包含中文和日文字符,如果单纯以shift_JIS存,日文可以正常写入硬盘,而由于中文字符在shift_jis中没有找到对应关系而导致存乱了

# 2.2 读乱了:如果硬盘中的数据是shift_JIS格式存储的,采GBK格式读入内存就读乱了

👉总结文件存、取乱码问题:👆

  • 保证存的时候不乱:
    • 致命性:存乱了具有致命性,也就是说当你存文件时使用的字符是日文,而当你存文件时使用的字符是日文,而你使用GBK存入硬盘,这个时候你的硬盘并没有对应的这种编码格式,这个时候当你保存存入硬盘重新打开文件,文件内容直接损坏了,数据也就不存在了。
    • 解决:在由内存写入硬盘时,你的文件中的字符,必须与存入硬盘所指定的编码格式有所对应。(比如:你使用GBK存入硬盘,但是你文件中输入字符是日文,这就不行。因为GBK只支持中文和英文字符。)
  • 保证读的时候不乱:
    • 致命性:取乱了并不一定致命,你当初用什么格式存的,那么你就用什么格式取就可以。
    • 解决:在由硬盘读入内存时,文件用什么编码格式存入硬盘的,就应该用什么编码格式读入内存。

1、文本编辑器nodpad++存取文本文件展示

1.1 以下步骤表示存文本时的乱码

  • 步骤:先不写内容,修改字符编码,复制内容👉(にほんご、嘿嘿嘿嘿你看不见我、English),保存到桌面,关闭,打开,把文件拖进去。

  • 当你存文件时使用的字符是日文,而你使用GB2312存入硬盘,这个时候你的硬盘并没有对应的这种编码格式,这个时候当你保存存入硬盘重新打开文件,文件内容直接损坏了,数据也就不存在了。如图👇:

  • 现在我用ctrl+s保存,重新打开文件,如图显示👇:

1.2 以下步骤表示取文本时的乱码

2、python解释器执行文件的前两个阶段

  • 执行py文件的前两个阶段就是python解释器读文本文件的过程,与文本编辑读文本文件的前两个阶段没人任何区别,要保证读不乱码,则必须将python解释器读文件时采用的编码方式设置为文件当初写入硬盘时的编码格式,如果没有设置,python解释器则会用默认的编码方式,在python3中默认为utf-8,在python2中默认为ASCII,我们可以通过指定文件头来修改默认的编码。

👉总结:👆

  • python3默认编码方式:utf-8
  • python2默认编码方式:ASCII
  • 为什么要指定解码文件头?
    • 如果你使用的是python3解释器,当python3解释器读取文件时(python3 D:test.py),如果没有指定解码文件头,python3解释器会使用它默认的utf-8的编码对照表去对应test.py这个存放在硬盘的二进制文件。当你存入硬盘的时候时使用GBK编码对照表的二进制形式去存入硬盘的,那么如果这个时候含有中文字符(中文字符在GBK中对应的是2个字节, utf-8对应的中文是3个字节)那么这个时候读取该文件时,就乱码了。
    • 如果你使的是python2解释器,当python2解释器读取文件时(python2 D:test.py),如果没有指定解码文件头,python2解释器会使用它默认的ASCII的编码对照表去对应test.py这个存放在硬盘的二进制文件,而我们所知ASCII只对应着英文字符。当你存入硬盘的时候使用GBK编码对照表的二进制形式去存入硬盘的,那么这个时候如果你的文件中含有中文字符(中文字符在GBK中对应的是2个字节, 而ASCII都没有中文字符的对应只会按照1个字节形式从二进制转换成ASCII码),那么这个时候读取该文件时,就乱码了。
  • 解决读取文件时乱码问题:在文件顶行写上# coding: 当初文件写入硬盘时采用的编码格式 或者使用右👉边这种花里胡哨的的玩法 # -*- coding: 当初文件写入硬盘时采用的编码格式 -*-
  • 区分读取文件头和读取文件内容的编码:
    • 读取文件头时:使用的是python解释器默认的编码
    • 读取文件内容时:使用的是文件头部指定的编码格式
    • 为什么读取文件头的时候不会乱码呢?我们需要知道无论是python2解释器还是python3解释器读取文件时,它们默认的英文编码字节对应都是一样的。(ASCII码对应英文字符1个字节,utf-8码对应英文字符也是一个字节)
  • 保证python程序前两个阶段不乱码的核心:
    • 你的文本编辑器默认是用什么编码格式存入硬盘的,你就需要指定什么格式的头部解码格式。(比如:你文本编辑器是用utf-8存,那么你就指定头部:#coding:utf-8)

3、python解释器执行文件的第三个阶段

  • 设置文件头的作用是保证运行python程序的前两个阶段不乱码,经过前两个阶段后py文件的内容都会以unicode格式存放于内存中。在经历第三个阶段时开始识别python语法,当遇到特定的语法name = '上'(代码本身也都全都是unicode格式存的)时,需要申请内存空间来存储字符串'上',这就又涉及到应该以什么编码存储‘上’的问题了。

    在Python3中,字符串类的值都是使用unicode格式来存储

    由于Python2的盛行是早于unicode的,因此在Python2中是按照文件头指定的编码来存储字符串类型的值的(如果文件头中没有指定编码,那么解释器会按照它自己默认的编码方式来存储‘上’),所以,这就有可能导致乱码问题

4、Python2中解释语法阶段(第三阶段)导致乱码问题👇

① 文件头指定gbk、使用pycharm终端(utf-8)

# coding: gbk
# python2中默认ASCII码,上面需要为其指定存入硬盘格式相同的编码格式

x = "上"
print([x], type(x))  # (['\xc9\xcf'], <type 'str'>) 提示:python2中要把x放在列表中打印,才会显示编码格式。直接放到print中,会像下面一样,会进行一个字符转换的处理。

print(x)        # ��
'''
上面为什么执行print(x)操作会乱码,显示成:��?
    打印操作就是将x的值,也就是把gbk格式的二进制('\xc9\xcf')交给终端(因为我们文件头指定gbk,所以定义变量时变量的值存入内存中使用的就是gbk编码),当终端接收到以后,发现并不是unicode(只有unicode才与字符有对应关系),但是终端任然会指定操作:gbk --> 解码 --> unicode格式的二进制,解码的过程终端会默认采用自己终端默认的编码。(pycharm的终端默认是utf-8格式,windows中的终端默认是gbk格式),所以该打印操作在pycharm中显示不正常,而在windows命令终端中显示正常。
    
   
    补充:为什么unicode与gbk编码对照表中是'\x49\x4f',而我们终端打印是'\xc9\xcf'?
    我们得从gbk编码的设计者开始说起,gbk对应英文我们知道是1个Bytes(8个进制位),对应中文是2个Bytes(16个二进制位)。
    那么我们想文件是躺在硬盘中对吧,在硬盘中它们全部都是0101啊,你又怎么知道,什么时候读取16个二进制,什么时候是读取8个二进制呢?而设计者们就发明了标识位,用来标识一次性读取二进制位的个数。
    那么怎么读取呢?我们知道1个字节是由8个0101组成,在区分到底是读取16个还是8个的时候,我们在起始位置时就开始判断。如果是该标识位是0,那么读取的就是一个英文字符,一共8个二进制(1bit+7bit=1Bytes)。如果该标识位是1,那么读取的就是一个中文字符,一共16个二进制(1bit+15bit=2Bytes).通过这种每次读取每次标识,完成了完美的区分中英文字符的对照关系。
'''
  • unicode关于汉字的对应关系表截图:👇
  • 终端打印中‘上’\xc9\xcf 与 unicode中‘上’的正真实数据\x49\x4f 转换关系图:👇

② 文件头指定gbk、使用windows终端(gbk)

③ 解决python2中str类型打印终端不乱码

  • 前言:python2中字符串有2种类型。一种str类型:x='上',一种unicode类型:x=u'上'
  • 保证python2得的字符串类型不乱码:我们要在字符串前面加小u。加小u在python2中是把这个字符串强制转换成unicode编码。(拓展补充:如果你的字符串中只包含数字或者字母,是可以不必要加小u)。
  • python3中为什么只有字符串类型,要不要加小u?python3中字符串前面帮我们默认加了小u,我们加不加都是一样,并不影响使用。

4、字符串encode编码与decode解码的使用

x = '上'  # python3中str类型默认前面就加了小u转成unicode,也就是说上默认在python3中在内存中存成了unicode格式我们把上看成unicode就可以了。(这里其实是由字符转换成unicode格式,这叫编码)

res = x.encode("gbk")  # 作用:1、使用encode指定gbk编码由内存写入硬盘。2、基于网络传输及存储。3、有可能要与老平台python2对接,直接unicode交给python2不行,我们这个时候就需要直接编码以后就可以直接丢给对方,对放就可以做相应的处理。{上面我们讲了编码解码的概念,上面说由unicode转换成其它编码格式gbk(GBK)这叫编码。由unicode转成了gbk。}
print(res)  # b'\xc9\xcf'

print(res.decode('gbk'))    # 使用decode指定gbk解码由内存读入硬盘由unicode对应字符显示。{由上面我们讲了编码解码的概念,上面说由其它编码格式gbk(GBK)转换成unicode这叫解码。}

# 注意:你使用什么格式编码的,你就得因该用什么格式解码。

5. 利用bytes解码与str编码

data = 'hello world'

# 字符串转二进制
data_bytes = bytes(data, encoding='utf-8')
print(data_bytes)  # b'hello world'

# 二进制转字符串
data = str(data_bytes, encoding='utf-8')
print(data)  # hello world

四. 总结

补充:字符编码只适合用文本文件。并不支持图片,视频等格式。

如何解决python2乱码问题?
	1. 指定文件头:`#coding:与文件存的编码格式一致`
	2. 在字符串前面加小u:` x=u"上"`
	
如何解决python3乱码问题? 
	1. 指定文件头:`#coding:与文件存的编码格式一致`

项目文件头2种写法(注意:只在你的执行主文件中添加即可,因为子文件最终还是会被调到主文件中执行。):
	1. 普通青年写法:`#coding:与文件存的编码格式一致`
	2. 花里胡哨的写法:`-*- #coding:与文件存的编码格式一致 -*-`

致那些年,我们依然没搞明白的编码: https://www.cnblogs.com/alex3714/articles/7550940.html

posted @ 2020-03-12 23:03  给你加马桶唱疏通  阅读(414)  评论(0编辑  收藏  举报