首页 > 其他分享 >字符编码

字符编码

时间:2024-04-08 14:45:18浏览次数:21  
标签:编码 UTF 字节 字符 二进制 Unicode

编解码

人类世界常见的语言文字多种多样,有英文字母例如a,有阿拉伯数字例如6,有中文例如好 等等。但是计算机的世界里面只有二进制即0和1,所以我们要存储和计算的时候就需要将人类世界的语言文字转换为计算机能识别的二进制,而人类的语言文字与计算机二进制相互转换的过程就是编解码。

ASCII

上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制之间的关系,做了统一规定被称为 ASCII 码。ASCII 码一共规定了128个字符的编码,例如大写的字母A是十进制65(二进制01000001),而计算机中一个字节(byte)有8位(bit),一位能表示一个二进制0或者1,所以一个字节能表示最多256个符号。但是ASCII只有128个符号,所以ASCII码只占用了一个字节的后面7位,最前面的一位统一规定为0。

GB2312

既然有了美国针对英语字符制定的ASCII码,那么为了能让计算机能处理中文,于是中国也制定了一套中文与二进制之间的关系编码,那就是中华人民共和国国家标准简体中文字符集。

其中流行比较广泛的就是GB2312。GB2312使用两个字节存储字符,采用区位码方法来表示字符所在的区和位。其中第一个字节称为“高位字节”,对应分区的编号,第二个字节称为“低位字节”,对应区段内的个别码位,GB2312标准共收录6763个汉字,同时收录了包括拉丁字母、希腊字母,日文平假名及片假名字母、俄语西里尔字母在内的682个字符。

GB2312的出现基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%的使用频率,但对于人名、古汉语等方面出现的罕用字和繁體字GB2312不能处理。因此后来又出现了GBK及GB18030汉字字符集以解决这些问题。

Unicode

美国有ASCII码,中国有GB2312,那韩国、日本等世界上各个国家都有自己的编码,同一个二进制数字可以被解释成不同的符号。因此要想正确读取一个字符,就必须知道它的编码方式,否则用错误的编码方式解码,就会出现乱码。

正因为世界各国都有自己的编码,导致程序很难适配所有编码。所以需要一种全世界通用的编码,将世界上所有的符号都纳入其中,为每一个字符都赋予一个独一无二的编码,采用统一的编解码就不会出现乱码,这就是 Unicode。

Unicode使用最多4个字节来表示,通常使用十六进制表示,即范围为00000000-FFFFFFFF。Unicode 是一个很大的集合,每个符号的编码都不一样。比如,U+0041表示英语的大写字母A、U+4E25表示汉字严。

Unicode规范:https://datatracker.ietf.org/doc/html/rfc3629#ref-UNICODE

UTF-8

有了Unicode统一全世界字符的编码,但Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。比如,汉字严的 Unicode是十六进制数4E25,对应二进制数100111000100101。这个二进制的表示至少需要2个字节,而目前Unicode最大4个字节,如果全部使用4个字节来进行存储,无疑会大大的浪费存储空间。

UTF-8 是 Unicode 的实现方式之一,采用一种变长的编码方式它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

UTF-8规范:https://datatracker.ietf.org/doc/html/rfc3629#section-3

UTF-8编码规则

  1. 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码
  2. 对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0
  3. 其余字节的前两位设为10,所有字节其他二进制位为这个符号的 Unicode 码
  4. 填充二进制位时从低位往高位填充即从右往左,不足位使用0进行填充
Unicode符号范围(十六进制) UTF-8编码方式(二进制)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

UTF-8规则解读

  1. 「为什么不直接存储Unicode对应的二进制,而是需要定义一套规则」

    假如UTF-8像ASCII码一样直接存储对应的二进制,比如汉字“好”对应的Unicode十六进制为597d,二进制为10110010 1111101。那么怎么区分这个二进制10110010 1111101,是一个Unicode字符而不是两个(101100101111101)呢?正因为Unicode字符转换成二进制最多可能占用4个字节,当超过一个字节的时候无法区分是多个单字节的字符还是单个多字节的字符,所以UTF-8不能直接像ASCII码一样直接存储对应的二进制。

  2. 「为什么只有一个字节的时候需要特殊处理用0开头」

    目前ASCII一共128个字符,从00000000到01111111,为了兼容ASCII所以只有一个字节的时候就用0开始。

  3. 「为什么第一字节要设计为n位填充1,n+1位填充0」

    为了区分一个多字节的编码,是「多个」单字节的字符?还是「单个」多字节的字符?节省空间的做法就是用编码的第一个字节的前几位来表示这个编码占用几个字节 至于为什么是用1而不是0? 假设用0来表示,汉字"好"的字节编码就是00000000 01011001 01111101。这样有一个问题就是无法正确识别出编码所占的字节数,所以还需要在表示字节数位和实际存储位中间设置一个分隔位,分割位取值简单做法就是与表示字节位数值取反即可。 比如某一个Unicode字符的字节编码是两字节的二进制00111111 11110000。这样又有一个问题就是该二进制的第一字节与单字节的规则冲突,所以设计多字节的的第一字节n位填充1,n+1作为分隔符与n位的填充符取反即为0。

  4. 「为什么n-1字节以10开头」

    上面的设计其实已经满足UTF-8的正常编解码了,但是还有一个问题。假设有一个二进制编码11100010 11000011 111001111 11011100 10001111 表示有两个字符。 第一个字符三个字节对应的二进制编码为11100010 11000011 111001111 ,第二个字符占两个字节对应的二进制编码位11011100 10001111。如果因为某些原因导致写入的时候出错了写成了11000010 11000011 111001111 11011100 10001111 。这时候读取程序就会识别为第一个字符两个字节(11000010 11000011),第二个字符三个字节(111001111 11011100 10001111)这样读取所有字符都是错的。 为了解决读取错误的问题,因为第一个字节包含字符字节数信息,所以只需要区分开编码的第一个字节和其他字节。读取包含字符字节信息的第一个字节错误时就能知道编码错误从而采取对应措施。 而已知第一个字节使用n位填充1,n+1位填充0的规则。所以非第一字节使用最少两个位10即可与第一字节的0(单字节)、110(二字节)、1110(三字节)、11110(四字节)区分开。 现在我们再看看,如果错误的写成了11000010 10000011 101001111 11011100 10001111 ,程序读取时错误的将该二进制识别为第一个字符两个字节(11000010 11000011)。当读取第二个字符时(111001111 11011100 10001111),即第三个字节(111001111)时,根据第一字节规则,字符的第一字节永远不可能为10,这时程序就知道这个编码错误了。就能进行对应的处理方式,提示错误或者跳过该字节继续往下读,如果继续往下读最多也就当前字符出错至少其他字符能正常读取,将错误率降至最低。

  5. 「为什么多余位填充0而不是1」

    试想一下,如果多余位填充1。汉字“祽” 对应的Unicode二进制为11110010 1111101 ,则对应的UTF-8二进制编码为11101111 10100101 10111101。而汉字“㥽”对应的Unicode二进制为11100101 111101 ,对应的UTF-8二进制编码为11101111 10100101 10111101。那么在解码的时候UTF-8二进制11101111 10100101 10111101。应该解码为汉字“祽”还是汉字“㥽”呢? 相反如果多余位填充0,那么汉字“祽”对应的UTF-8二进制编码为11100111 10100101 10111101,而汉字“㥽”对应的UTF-8二进制编码为11100011 10100101 10111101,就不会出现编码冲突的问题。

标签:编码,UTF,字节,字符,二进制,Unicode
From: https://www.cnblogs.com/lvlaotou/p/18121107

相关文章

  • 字符串进阶-字符串函数
    字符串进阶-字符串函数应用c++提供了大量的字符串函数,供我们在解题时使用。一、常用函数介绍1-长度(有返回值)a.size()或a.length()2-查找(有返回值)a.find("hello")//返回子串hello在a中第一次出现时开头字母h的下标a.find('h')//返回字符h在a中第一次出......
  • 题目 1035: [编程入门]自定义函数之字符类型统计
    一、题目 题目描述编写一函数,由实参传来一个字符串,统计此字符串中字母、数字、空格和其它字符的个数,在主函数中输入字符串以及输出上述结果。只要结果,别输出什么提示信息。输入格式一行字符串输出格式统计数据,4个数字,空格分开。样例输入!@#$%^QWERT   1234567......
  • 数字电子基础——编码器
    编码器编码:用文字、符号或数字表示特定对象的过程。在数字电路中,采用二进制进行编码编码器:实现编码功能的电路二进制编码器用nnn位二进制代码对......
  • 字符串的扩展
    字符串的扩展字符的Unicode表示法ES6加强了对Unicode的支持,允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的Unicode码点。但是,这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表示"\uD842\uDFB7"//"......
  • 字符串的新增方法
    字符串的新增方法String.fromCodePoint()ES5提供String.fromCharCode()方法,用于从Unicode码点返回对应字符,但是这个方法不能识别码点大于0xFFFF的字符String.fromCharCode(0x20BB7)//"ஷ"String.fromCharCode()不能识别大于0xFFFF的码点,所以0x20BB7就发生了溢出,最高......
  • 深入了解图片Base64编码
    title:深入了解图片Base64编码date:2024/4/810:03:22updated:2024/4/810:03:22tags:Base64编码图片转换HTTP请求前端开发移动应用性能优化图片压缩1.什么是Base64编码Base64编码是一种将二进制数据转换为文本字符串的编码方式,通过将数据转换为一种可打印的......
  • 字符串哈希板子
    #include<iostream>#include<cstring>#defineMAX_SIZE100usingnamespacestd;classStringHash{public:intsize;char*array;char*array_forward;unsignedlonglong*pre_base;unsignedlonglong*hash_array;uns......
  • python学习--基础知识(字符串扩展)
    八、字符串扩展1、字符串的三种定义方式2、字符串的拼接3、字符串的格式化4、字符串格式化的精确度控制5、字符串格式化的快速方法6、字符串格式化--对表达式进行格式化......
  • 无重复字符的最长子串
    给定一个字符串s,找出其中不包含重复字符的最长子串的长度。例如字符串abcabcd子串分为abc abcdabcd的长度最长。思路:将一个字符串分为多个子串,用二维数组存储起来,最后利用strlen()获取子串长度,超出最长子串。第一阶段:将字符串分为多个子串。可以用二维数组来存储子串,暂用in......
  • 【MATLAB源码-第173期】基于matlab的RS编码的2FSK通信系统误码率仿真,通过AWGN信道输出
    操作环境:MATLAB2022a1、算法描述通信系统的基本框架在现代通信系统中,数据的传输通常涉及四个基本步骤:源编码、信道编码、调制和传输。源编码主要负责压缩数据,减少传输的数据量。信道编码则通过添加冗余信息来提高传输数据的可靠性。调制是将数字信号转换为适合在物理信道......