• 038 字符编码


    一、什么是字符编码

    计算机实际上只能识别机器语言,也就是0和1,也就是二进制(现实生活中,通常我们使用十进制来表示一个数,这是因为我们只有10个手指头。)

    那么0和1就分别对应着电气特性中的高低电平,计算机就是通过识别高低电平来区分0和1。

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

    • 首先,必须将字符信息进行编码。编码成计算机所能理解并识别的机器语言
    • 其次,不同国家之前的编码格式并不相同。想要在计算机中和不同国家的人进行交流就应该让人家能看懂你想要表达的信息。这就对应着字符的编解码问题

    这就是我们要说的 字符编码

    1.1 字符编码发展史与分类

    计算机由美国人发明,最早的字符编码为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(Unicode Transformation Format-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编码下继续工作。

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

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

    内存中还使用Unicode编码,是因为历史遗留问题造成的,但是因为现在写代码使用的都是UTF-8代码,所以以后内存中的代码都将变成UTF-8代码,并且以前遗留的各个国家的代码都将被淘汰,所以未来内存中使用的编码也将使用UTF-8编码替代Unicode编码。

    三、乱码分析

    首先明确概念

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

    乱码的两种情况:

    • 乱码一:存文件时就已经乱码

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

    • 乱码二:存文件时不乱码而读文件时乱码

    存文件时用utf-8编码,保证兼容万国,不会乱码,而读文件时选择了错误的解码方式,比如gbk,则在读阶段发生乱码,读阶段发生乱码是可以解决的,选对正确的解码方式就ok了。

    四、Python2和Python3字符编码的区别

    Python2中默认使用ascii,Python3中默认使用utf-8

    如果指定了#coding:utf-8#-*-coding:utf-8-*-,这会决定以什么编码格式将代码读入内存,这一行就是设定Python解释器这个软件使用的编码格式。

    **在Python2中如果指定了字符编码,那么内存存取就会按照指定的字符编码去存取。解释或去执行时就要按照指定了的字符编码去解释。否则就会乱码。 **

    如:

    #coding:gbk
    name = "爸爸"
    

    但在Python3中就不会有这样的问题,因为无论你指定了什么字符编码,在内存存取时都会使用Unicode编码去存取,Unicode编码可以和任意的字符编码相互转换,并在读取时按照所需的编码去读取,这样就很好解决了字符编码的问题

    4.1 Python2中的Unicode类型

    在Python2中实际上也可以将字符串通过Unicode编码进行存储,这样也可以避免字符编解码不统一的问题。

    只需要在定义字符串的内容前加u即可

    如:

    #coding:gbk
    name = u"爸爸"
    

    这样存储时也是按照Unicode编码进行存储

    五、总结

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

    其实说了半天我就像说一句话,牢记这句话:

    按什么编码存,就按什么编码取!这样一定没问题!!!

  • 相关阅读:
    我的大学学习之路
    拉勾上的一道题目
    python中文处理之encode/decode函数
    几个容易出错的css盒子模型细节
    洗牌算法shuffle
    判断正整数是否对称
    一种快速求fibonacci第n个数的算法
    利用正则表达式作为string.split seprator
    docker部分命令
    idea上传项目到GitHub
  • 原文地址:https://www.cnblogs.com/XuChengNotes/p/11317186.html
Copyright © 2020-2023  润新知