22.字符编码

一、基础知识

  1. 软件运行前,软件的代码及其相关数据都存放于硬盘中
  2. 任何软件的启动都是将数据从硬盘中读入内存,然后cpu从内存中取出指令并执行
  3. 软件运行过程中产生的数据最先都是存故于内存中的,若想永久保存软件产生的数据,则需要将数据由内存刷入硬盘

二、 文本编辑器存取文件的原理

  1. 打开编辑器就启动了一个进程是在内存中的,所以用编辑器编写的内容,也都是存放在内存中的,断电后数据丢失。
  2. 要想永久保存需要点击保存按钮:编辑器把内存的数据刷到硬盘上。
  3. 我们编写的一个py文件在没有执行前和编写其它文件没有任何区别,都只是在编写一堆字符而已。

三、Python解释器执行py文件的原理

  • 第一阶段:Python解释器启动,此时相当于启动了一个文本编辑器
  • 第二阶段:Python解释器相当于文本编辑器,去打开test.py文件,从硬盘上将test.py的文件内容读取到内存中。
  • 第三阶段:Python解释器会解释执行刚刚加载到内存中的test.py的代码(ps:在该阶段,即真正执行代码时,才会识别Python的语法,执行文件内代码,当执行到name='lwx'时,才会开辟内存空间去存放字符串'lwx')

四、 Python解释器与文件编辑器的异同

  • 相同点:Python解释器吧是执行文件内容的,因而Python解释器具备读取py文件的功能,这一点与文本编辑器不同
  • 不通点:文本编辑器将文件内容读写到内存后,是为了显示或者编辑,根本不会例会Python的语法,二Python解释器将文件读写到内存中,是为了执行Python代码,会执行Python语法

五、字符编码介绍

5.1 什么是字符编码

计算机要想工作必须通电,即用‘电’驱使计算机干活,也就是说‘电’的特性决定了计算机的特性。电的特性即高低电平(人类从逻辑上将二进制数1对应高电平,二进制数0对应低电平),关于磁盘的磁特性也是同样的道理。
结论:计算机只认识数字。很明显,我们平时在使用计算机时,用的都是人类能读懂的字符(用高级语言编程的结果也无非是在文件内写了一堆字符),如何能让计算机读懂人类的字符?它必须经过一个过程:
字符 -------->翻译过程 ------->数字
总而言之,字符编码就是将人类的字符编码成计算机能识别的数字,这种转换必须遵循一套固定的标准,该标准无非是人类字符与数字的对应关系,称之为字符编码。

5.2 涉及到字符编码的两个场景

  1. 一个Python文件中的内容是由一堆字符组成的,存取设计到字符码问题(Python文件并未执行,前两个阶段均属于该范畴)
  2. Python中的数据类型字符串是由一串字符组成的(Python文件执行时,即第三阶段)

5.3 字符编码发展史与分类

5.3.1. ASCII

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

5.3.2 多国编码

当然我们编程语言都用英文没问题,ASCII够用,但是在处理数据时,不同的国家有不同的语言,中国人会加入中文,日本人会在自己的程序中加入日文,韩国人也是。但是要表示中文,单拿一个字节表表示一个汉子,是不可能表达完的(连小学生都认识两千多个汉字),解决方法只有一个,就是一个字节用>8位2进制代表,位数越多,代表的变化就多,这样,就可以尽可能多的表达出不通的汉字,所以中国人规定了自己的标准gb2312编码,规定了包含中文在内的字符与数字的对应关系。日本人规定了自己的Shift_JIS编码;韩国人规定了自己的Euc-kr编码(另外,韩国人说,计算机是他们发明的,要求世界统一用韩国编码,但世界人民没有搭理他们)

5.3.3 Unicode

​ 这时候问题出现了,精通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进行相互转换

5.3.4 UTF-8

这时候乱码问题消失了,所有的文档我们都使用但是新问题出现了,如果我们的文档通篇都是英文,你用Unicode会比ascii耗费多一倍的空间,在存储和传输上十分的低效。本着节约的精神,又出现了把Unicode编码转化为“可变长编码”的UTF-8(Unicode Transformation Format-8)编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间(UTF-8实现的是整体上精简。部分字符会出现占用空间较大):

UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,也是一种前缀码,又称万国码。由Ken Thompson于1992年创建。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码

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

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

优点:
UTF-8编码可以通过屏蔽位和移位操作快速读写。字符串比较时strcmp()和wcscmp()的返回结果相同,因此使排序变得更加容易。字节FF和FE在UTF-8编码中永远不会出现,因此他们可以用来表明UTF-16或UTF-32文本(见BOM)UTF-8是字节顺序无关的。它的字节顺序在所有系统中都是一样的,因此它实际上并不需要BOM。

缺点:
你无法从UNICODE字符数判断出UTF-8文本的字节数,因为UTF-8是一种变长编码它需要用2个字节编码那些用扩展ASCII字符集只需1个字节的字符ISOLatin-1是UNICODE的子集,但不是UTF-8的子集8位字符的UTF-8编码会被email网关过滤,因为internet信息最初设计为7位ASCII码。因此产生了UTF-7编码。UTF-8在它的表示中使用值100xxxxx的几率超过50%,而现存的实现如ISO2022,4873,6429,和8859系统,会把它错认为是C1控制码。因此产生了UTF-7.5编码。

5.4 内存为什么不用UTF-8呢?

说了那么一大堆,那为什么内存用Unicode,而不直接使用UTF-8呢?这样不就可以直接把代码从内存直接丢入硬盘了吗?出现这个问题的原因是硬盘中还躺了其他国家的代码,各个国家的代码的二进制还需要运行在计算机上使用,因此内存中必须使用Unicode的编码,因为Unicode能和硬盘中其他国家的二进制中的代码进行转换,但是UTF-8只是简化了代码的存储,它并不能与其他国家硬盘中的代码进行关系转换。总而言之只有Unicode编码才能运行其他国家硬盘中的代码,而UTF-8的代码无法进行该操作。
内存中还使用Unicode编码,是因为历史遗留问题造成的,但是因为现在写代码使用的都是UTF-8代码,所以以后内存中的代码都将变成UTF-8代码,并且以前遗留的各个国家的代码都将被淘汰,所以未来内存中使用的编码也将使用UTF-8编码替代Unicode编码。

5.5 字符编码之文本编辑器操作

5.6 乱码分析

首先明确概念

  • 文件从内存刷到硬盘的操作简称存文件
  • 文件从硬盘读到内存的操作简称读文件

乱码的两种情况:

  • 乱码一:存文件时就已经乱码
    存文件时,由于文件内有各个国家的文字,我们单以shiftjis去存,
    本质上其他国家的文字由于在shiftjis中没有找到对应关系而导致存储失败。但当我们硬要存的时候,编辑并不会报错(难道你的编码错误,编辑器这个软件就跟着崩溃了吗???),但毫无疑问,不能存而硬存,肯定是乱存了,即存文件阶段就已经发生乱码,而当我们用shiftjis打开文件时,日文可以正常显示,而中文则乱码了。
  • 乱码二:存文件时不乱码而读文件时乱码
    存文件时用utf-8编码,保证兼容万国,不会乱码,而读文件时选择了错误的解码方式,比如gbk,则在读阶段发生乱码,读阶段发生乱码是可以解决的,选对正确的解码方式就ok了

避免乱码方式:字符按照什么标准而编码的,就要按照什么标准解码,此处的标准指的就是字符编码

六、Python2和3字符编码的区别

区别点 python2 python3
print 是一个语法结构 是一个函数,print(‘hello world’)
编码问题 pyhton2诞生的时候没有Unicode,所以python2使用的默认编码格式是asscii编码,这也是python2经常出现编码问题的原因之一 python3中程序默认数据类型为Unicode,所以直接将数据编码(encode)成为想要转换的数据类型(gbk,utf-8,gb18030,gb2312),然后解码成为对应的数据类型显示在屏幕上
除法运算 整数相除的结果是一个整数,把小数部分完全忽略掉,浮点数相除会保留小数点的部分得到一个浮点数的结果。 如:>>> 1 / 2
0
>>> 1.0 / 2.0
0.5
对于整数之间的相除,结果也会是浮点数, 如:>>> 1/2
0.5
input python2中的raw_input()与python3中的input()功能一样,raw_input()会把用户输入的任何值都作为字符串来对待;而python2中的input不能读取非字符串 Python3 中只有 input(),并且把用户输入的任何值都作为字符串来对待。
不等运算符 Python 2.x中不等于有两种写法 != 和 <> Python 3.x中去掉了<>, 只有!=一种写法
  1. 打开python解释器
  2. python解释器相当于文本编辑器,读取二进制转化为普通字符 a = 1
  3. 对转化后的普通字符进行解释(定义变量就要新开辟内存空间存放变量)

6.1 python2

Python2使用指定的编码读文件。用文件指定的编码方式存储定义后的变量,如果文件指定编码为'gbk' ,那就会以gbk的形式存储变量, 本来打印的是0和1,但是终端会自动对你的0和1安装终端默认的编码转换成字符 ,如果终端的默认编码是utf8 ,乱码; 如果终端默认编码是gbk,不乱码.如果定义变量前加上u,coding:xxx不会对他造成任何影响, 因为会用unicode编码存储变量, 终端是任何类型的编码都可以识别

6.2 python3

用unicode编码方式存储定义后的变量
Python默认使用UTF-8编码读文件

七、总结

  1. 保证不乱码的核心法则就是,字符按照什么标准而编码的,就要按照什么标准解码,此处的标准指的就是字符编码。
  2. 内存固定使用unicode, 我们可以改变的是存入硬盘采用格式,即:在内存中写的所有字符,一视同仁,都是Unicode编码,比如我们打开编辑器,输入一个“你”,我们并不能说“你”就是一个汉字,此时它仅仅只是一个符号,该符号可能很多国家都在使用,根据我们使用的输入法不同这个字的样式可能也不太一样。只有在我们往硬盘保存或者基于网络传输时,才能确定”你“到底是一个汉字,还是一个日本字,这就是Unicode转换成其他编码格式的过程了。简而言之,就是内存中固定使用的就是Uncidoe编码,我们唯一能改变的就是存储到硬盘时使用的编码。
  • Unicode----->encode(编码)-------->gbk
  • Unicode<--------decode(解码)<----------gbk
posted @ 2020-03-12 18:23  祥SHAO  阅读(281)  评论(0编辑  收藏  举报