字节、字符与字符编码的区别与联系

字节、字符与字符编码的区别与联系

字节

位(bit)是计算机中信息的最小单元。位是由电路实现的,硬件底层使用数字电路,以电压的高低作为记录信息的方式:较高的电压表示数值“1”,较低的电压表示数字“0”。因此,一个位有两种可能的值,二进制是一种非常适合表示计算机存储数据的方式。

计算机使用字节作为最小的存储单元,大多数计算机上,一个字节等于 8 个位,因此一个字节能表示的数字范围为 0 - 255 ,或二进制形式下的 0b0000 0000 - 0b1111 1111 。如果采用 16 进制表示,那么可以很方便地表示为 0x00 - 0xFF 。

多个字节按顺序组合,便可以组成任意数据。

字符与编码

ASCII编码

字节只能表示一个数字,为了让人们可以更好地明白其中的含义,需要将其解读为文字符号,即字符,然后显示在屏幕上。例如,如果一个字节中存储的数据是 0x08 ,如果计算机将其解读为第八个英文字母,那么计算机处理之后可以在屏幕上显示“h”;如果连续 5 个字节中存储的数据是 [0x08, 0x05, 0x0C, 0x0C, 0x0E] ,计算机处理之后就可以根据类似的规则转换为连续的英文字符“hello”,根据这种方式就可以将计算机中保存的数据转换为人们容易理解的形式。

根据以上思路,人们制定了 ASCII(American Standard Code for Information Interchange, 美国标准信息交换代码),使用如下图所示的数值-字符对应关系表:
在这里插入图片描述

ASCII 是一种编码规则,利用该规则,便可以利用数字替换实际的形状符号。特别地,数值 0 - 31 表示控制字符,而不是实际可显示的形状符号。例如,当计算机处理时遇到数值 0x07 ,它会控制某些设备发出响铃,而不是控制屏幕显示符号;当计算机遇到遇到数值 0x10 ,那么就表示一行的结束,应该控制屏幕从下一行开始显示字符。

这种编码规则是双向的,对字符编码(encoding)可以将字符串转换为字节形式的数据;而对字节数据解码(decoding)则可以将字节形式的数据转换为字符串,并显示出来。

ASCII 使用的字符代码范围从 0 到 127 ,对应 7 个二进制位,完全可以使用一个字节存储一个字符代码。这种情况下多余的 1 个二进制位还可以用作校验,检查这个字符在存储的过程中有没有发生意外变动。

GBK编码

ASCII 码可以表示出所有的英文字符、数字和常用标点,但是一个字节最多只有 256 种不同的值,尽管 ASCII 只使用了其中的一半,但还是不足以容纳其它语言的字符。常用的汉字大约有 3000 个,已经远远超出了一个字节的容纳能力了,因此这种情况下需要规定一套新的字符编码规则。

为了能表示更多的字符,许多编码方案往往用多个字节来指代一个字符。GB2312 是一种用于表示汉字的编码方案,它扩展自 ASCII 码,使用两个字节来表示一个汉字。

为了兼容 ASCII 码,一个数值小于等于 127 的字符的意义与原来相同,但两个连续的、数值大于 127 的字符连在一起时,就表示一个汉字。但是 GB2312 也没有完全用尽两个字节,前面的一个字节(称为高字节)的范围从 0xA1 至 0xF7 ,后一个字节(称为低字节)的范围从 0xA1 到 0xFE ,组合起来可以表达出大约 7000 多个简体汉字,以及必要的拼音符号、希腊字母、甚至日文假名都包括在内。

为了区分中文的标点和英文的标点,GB2312 编码为数字、标点、字母也重新编排了两个字节长的编码,也就是输入法中存在的“全角”字符。而兼容的 ASCII 中存在的标点符号就是“半角”字符。

为了表示更多的字符,GB2312 后来再次被扩展,将两个字节中的所有组合全部使用,发展为了 GBK 编码。GBK 向下兼容 GB2312 的所有编码情况的同时,增加了许多生僻的汉字和不太常用的符号。

GB2312 和 GBK 是对 ASCII 的中文扩展,不同的国家或地区都针对本国语言产生的新的编码标准。基于这种用两个或多个字节表示一个字符的编码思路,台湾地区针对繁体推出了 Big5 编码方案,日本针对日文与日文汉字推出了 Shift_JIS 编码方案,欧洲地区也有类似的编码方案。它们都兼容 ASCII 编码,也就是说如果一个字节的值小于等于 127 ,那么就按 ASCII 编码处理;如果大于 127 ,那么就和后续的字节一起当做当前编码的内容处理。

在 Windows 等系统上也可以看到一种称为“ANSI”的编码,实际上这是一种误称,实际代表的是系统当前使用的本地化编码方案。如果改变系统设置里的区域,相应的编码方案也将改变。

Unicode

Unicode字符集
随着互联网的兴起,世界各地的信息开始在互联网上流通。这时,不同的编码方案就免不了发生碰撞。一串相同的字节序列使用不同的编码解码,得到的信息是完全不一样的。例如,中文 “你好” 如果使用 GBK 编码,得到的字节序列为 [0xc4, 0xe3, 0xba, 0xc3] ,但反过来如果使用 Big5 对这串字节序列解码,得到的内容为 “斕疑” ,如果使用 Shift_JIS 解码,得到的内容为 “ト羲テ” 。这也就导致不同地区发送的字节形式的消息,如果被另一个地区接收到,那么使用另一个地区的编码对其处理后的结果完全没法阅读,这就是通常看到的“乱码”。

因此,ISO(International Organization for Standardization, 国际标谁化组织)重新制定了一套完整的编码方案,称为 Universal Coded Character Set ,俗称 Unicode 或“万国码”。Unicode 在设计时,包括了地球上所有文字和和符号,甚至还有多余的编号保存简单的表情(emoji)。

Unicode 使用两个字节统一表示所有的字符,包括原有的 ASCII 字符也不例外。不过原有的 ASCII 除了由一个字节扩展成两个字节外,还是按原有的顺序占据 U+0000 - U+007F 这最前面的位置。除此之外,其它语言文字也按照一定规则被编排进去,例如汉字占据其中的 U+4E00 - U+9FA5 这些码位。

完整的 Unicode 字符表可以在 https://home.unicode.org/ 中找到,以下展示了部分 Unicode 字符:

在这里插入图片描述

Unicode 从诞生起也一直在发展。最早的 Unicode 使用两个字节来表示为一个字符,这种标准称为 UCS-2(其中 UCS 指 Unicode Character Set),因此总共可以组合出 65535 不同的字符。然而 Unicode 的野心是很大的,它除了表示文字、符号之外,还想表示一个文字的多种不同变体形式。例如,一个汉字有简体和繁体两种表示形式,而繁体的汉字在香港、台湾和日本的写法可能略微有差别的。再加上 Unicode 包含了许多表情符号,因此这 65535 个字符很快就不够用了。

基于此,Unicode 提供了 UCS-4 方案,使用四个字节来表示一个字符。UCS-4 是向下兼容 UCS-2 的,其中最高字节的最高位为 0 ,其它 7 位将 Unicode 字符集划分为了 128 个组(group),每个组再根据次高字节划分为 256 个平面(plane),每个平面根据次低字节划分为 256 行(row),每行有 256 个码位(cell),这样就可以组合出约 21 亿个不同的字符出来,这下完全够用了。

UCS-4 不仅是完全够用,甚至还过于冗余,这 21 亿个码位不可能用的完,更不可能一下塞满,只能根据需求向上添加。截止 2021 年 9 月,Unicode 只编排了 144,697 个字符,分布在平面 0 、平面 1 、平面 2 、平面 14 、平面 15 和平面 16 上。其中平面 15 和平面 16 只作为专用区使用。所谓专用区,就是保留给大家放自定义字符的区域。例如,苹果公司就在专用区上定义了一系列的公司图标,但这些图标在 Android 和 Windows 平台都无法正确显示。

UTF编码方案

目前的 Unicode 连平面都只使用了一个零头,更用不到组了。这种使用 4 个字节存储一个字符的形式显然过于浪费,尤其是对于只需要使用 ASCII 字符的场景来说,足足有 3 倍的空间浪费。在互联网传输数据时,过多冗余的信息会造成传输缓慢、收发效率低下的问题。基于此,Unicode 提出了 UCS Transformation Format ,简称 UTF ,旨在对现有的 Unicode 字符集重新再次编码,再次编码后的字符可以按照一定规则重新解码为 Unicode 字符集。

最简单的编码方案称为 UTF-32 ,即直接使用 UCS-4 的四字节共 32 位的形式。这种形式的编码压根没有减少字符占用的空间,因此并不实用。Unicode 实际只使用了 32 位中的 21 位,既然 Unicode 并没有用到组,那么这部分空间完全可以压缩。Unicode 也只用了平面的一个零头,这部分空间如果能进一步被压缩,那显然是更好的。

但是再怎么压缩,存储的最小单元是字节,为了支持到平面这个维度,最小也只能压缩到 3 字节,这种情况下对于多数只需要使用 ASCII 字符的场景来说,还是有 2 倍的空间浪费。

UTF-16

基于此,一种变长的编码方案 UTF-16 被提出:对于 Unicode 码小于 U+10000 的字符(也就是原先的 UCS-2 字符),使用 2 个字节直接存储,不用进行编码转换;但对于 U+10000 - U+10FFFF 之间的字符,则需要使用 4 个字节存储:首先将字符对应的 Unicode 码 减去 0x10000 ,得到的结果不超过 20 位,再将这 20 位分为高 10 位和低 10 位,分别塞进前两个字节的低 10 位和后两个字节的低 10 位中。并且前两个字节剩余的 6 个二进制位固定为 0b110110 ,后两个字节剩余的 6 个二进制位固定为 0b110111 。

下表展示了 UTF-16 对 Unicode 的编码规则:

Unicode 编码(十六进制)Unicode 编码(有效二进制)UTF-16编码方式
0x0000 0000 - 0x0000 FFFF0b xxxxxxxx xxxxxxxx0b xxxxxxxx xxxxxxxx
0x0001 0000 - 0x0010 FFFF0b yyyy yyyyyyxx xxxxxxxx0b 110110yy yyyyyyyy 110111xx xxxxxxxx

同时,为了区分两个字节的 UTF-16 和四个字节的 UTF-16 ,Unicode 还专门保留了 U+D800( 0xD8 的二进制为 0b110110 00 )到 U+DFFF( 0xDF 的二进制为 0b110111 11 )不被任何字符使用,这样只要解析到 0b110110yy 形式的字节或 0b110111xx 形式的字节,就可以认为这是一个四字节的 UTF-16 了。

这种编码方案十分复杂,但它至少成功压缩了 Unicode 编码的存储。由于目前最多只使用到第 16(即第 0x10 )平面,因此 UTF-16 可以完全对目前所有的 Unicode 字符编码。

Big Endian与Little Endian

在网络发送数据时,还有一个问题需要解决:一个字符有多个字节,但在发送时只能一个字节一个字节发送。操作系统可能有两种发送方式:可以先发送最高字节,也可以先发送最低字节。这两种发送方式会造成数据的解读不同,从而破坏 Unicode 编码。

不过 Unicode 定义了一个专门的控制符:零宽度非换行空格(zero width no-break space ,它的 Unicode 码位为 U+FEFF ,如果先发送高位(这种发送方式称为 big endian ,大端法),那么目标会先接收到 0xFE 字节;反之如果先发送低位(这种发送方式称为 little endian ,小端法),那么目标会先接收到 0xFF 字节。

这样,只要根据最先接收到的字符,就可以判断是高位先发送还是低位先发送。这种用来判断哪个字节先发送的特殊字符称为 byte order mark (BOM)。在本地使用 UTF-16 存储文本文件时,也需要在文件头带上 BOM 。

UTF-8

UTF-16 尽管对编码实现了一次压缩,但是这种使用二或四字节的形式还是有一些问题,尤其是 ASCII 码也被强制编排为两字节,这对于大多数使用 ASCII 的情况还是不够友好。不过,Unicode 还存在一种更强大的编码方案:UTF-8 。

UTF-8 使用控制位与字符位组成,控制位表示当前字符占据的字节数,字符位则对应真正的 Unicode 字符位。其中:

如果一个字节的最高位为 0 ,那么该字符就占据一个字节,剩下的 7 位则完全对应 7 位的 ASCII 码
对于 n 字节字符,第 1 个字节的最高 n 位为 1 ,第 n+1 位为 0 ,并且其它字节的最高两位都为 10
下表展示了 UTF-8 与 Unicode 的转换关系:

UnicodeUTF-8
U+0000 - U+007F0b0xxxxxxx
U+0080 - U+07FF0b110xxxxx 10xxxxxx
U+0800 - U+FFFF0b1110xxxx 10xxxxxx 10xxxxxx
U+10000 - U+10FFFF0b11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

UTF-8 这种变长的形式完全兼容 ASCII ,这是它最大的特点。仔细观察 UTF-8 的二进制位可以发现,UTF-8 甚至不需要 BOM ,因为除去单字节的情况不考虑,多字节情况下只有最高字节的次高二进制位才是 1 。

UTF-8 目前是世界上使用最广泛的 Unicode 编码方案,几乎所有的互联网网站都使用 UTF-8 编码,大多数 Linux 系统的默认编码也为 UTF-8 。UTF-8 与 Unicode 一起,构成了现代编码方案的基础。

当然,UTF-8 也是有缺点的:尽管目前 UTF-8 可以使用一个到四个字节灵活地表示所有的 Unicode 编码,但是如果 Unicode 再往上添加,UTF-8 也不可避免地需要再次添加长度,甚至最长需要 6 个字节才能表示 UCS-4 的所有情况。不过,在可遇见的将来,人们都不需要这么多字符。

Unicode 与编码经过了一个很长的发展过程,人们为了统一编码附出了许多努力。在此过程中出现了许多编码方案的尝试。为了保持编码的兼容,也付出过惨痛的代价:Windows 系统为了让二十年前还没有使用 UTF-8 编码的程序不出现乱码的情况,至今系统的默认编码都不是 UTF-8 。

Unicode 也是在不断发展的,除了继续向 Unicode 添加字符以外,也有尝试过多种 Unicode 的替代编码方案。GB18030 是一种针对汉字的编码改善方案,它在完全兼容 GB2312(部分兼容 GBK )的同时,也添加了对 Unicode 的支持。总之,为了规范编码,人们还有很长的一段路要走。

参考资料/延伸阅读

https://en.wikipedia.org/wiki/Universal_Coded_Character_Set
https://en.wikipedia.org/wiki/Unicode
https://baike.baidu.com/item/%E7%BB%9F%E4%B8%80%E7%A0%81/2985798
https://en.wikipedia.org/wiki/UTF-16
https://en.wikipedia.org/wiki/UTF-8
https://en.wikipedia.org/wiki/Byte_order_mark
https://unicode.org/faq/utf_bom.html
https://www.ibm.com/docs/en/aix/7.2?topic=globalization-code-sets-multicultural-support

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/326078.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微信预约挂号系统源码,公众服务号(小程序)预约挂号,适用于各级公立和民营医院,用来搭建互联网医院平台,可对接院内his、lis、pacs系统

掌上智慧医院、公众服务号(小程序)预约挂号、互联网平台源码 系统介绍 “移动智慧医院”平台既可以让患者足不出户就可以利用微信进行在线挂号,实现分时段就诊,就诊后也可以直接使用手机微信缴费,还可以通过微信实现查…

NFS网络共享存储服务技术攻略

目录 一.NFS 1.定义 2.特点 3.原理 二.服务端NFS配置文件 1.主配置文件 2.文件格式 3.相关命令 三.实验:NFS共享存储服务配置 1.服务端安装nfs-utils和rpcbind软件包 2.服务端新建共享目录给权限 3.服务端修改配置文件/etc/exports 4.服务端关闭防火墙…

FlinkAPI开发之水位线(Watermark)

案例用到的测试数据请参考文章: Flink自定义Source模拟数据流 原文链接:https://blog.csdn.net/m0_52606060/article/details/135436048 Flink中的时间语义 哪种时间语义更重要 从《星球大战》说起 数据处理系统中的时间语义 在实际应用中&#xff0c…

CC工具箱使用指南:【获取所有字段信息】

一、简介 这个工具的目的简单易懂,就是获取选定要素图层的所有字段信息。 本身不对要素图层作任何处理,只是一个查看属性的工具。 问我要用在什么地方,我也不知道-_- 二、工具参数介绍 点击【信息获取】组里的【获取所有字段信息】工具&a…

解决flask中jinja2插值变量变成字符串的办法

今天在通过使用{{ variable_name }}这种方式插入html内容时,发现变量内容到了页面中全部变成了字符串, python代码: return render_template(FilePath.file_path_to_page,md_contenthtml_content # 返回html内容 )html代码中插入&#xff1…

【运维杂谈】为什么docker镜像推送至harbor上就变小了?

为什么docker镜像推送至harbor上就变小了?我们以一个游戏镜像为例,在Linux显示295MB。 [rootWorker232 ~]# docker images | grep v0.6 harbor.koten.com/koten-games/games v0.6 30ec3e6e4747 25 hours ago 295MB […

C# Cad2016二次开发HelloWorld(一)

1 新建类库 二 引用 acdbmgd.dll、acmgd.dll、accoremgd.dll 三 HelloWorld代码 public class Class1{/// <summary>/// 程序入口标识/// </summary>[CommandMethod("HelloWorld")]public void HelloWorld(){Document adoc Autodesk.AutoCAD.Applicatio…

presto 支持regexp_count

一、背景 1、查询regexp_count 函数提示未注册 用户想正则查询特定字符出现次数 function regexp_count not registered 二、调研 1、官网地址&#xff1a; Presto Documentation — Presto 0.284 Documentation 2、regexp_extract_all Regular Expression Functions —…

如何为 SEO 进行关键字研究

什么是关键词研究&#xff1f; 关键字研究是查找和分析理想网站访问者输入搜索引擎的关键字的过程。这使您能够在内容策略中定位最有效的关键字。 关键字是人们用来在搜索引擎中查找信息或产品的单词或短语。例如&#xff0c;如果您想为您的小狗购买食物&#xff0c;您可以在…

最简单爱心的解析

首先你需要了解爱心代码在直角坐标系的方程 数学知识&#xff1a;x 属于 -1.5 ~ 1.5 y 属于 -1 ~ 1.5 和 高中所学的线性规划 请看代码 #include <math.h> #include <stdlib.h> #include <Windows.h> #include <stdio.h> int main() { …

迅软科技丨IT企业如何应对数据泄密危机?

随着信息技术的快速发展&#xff0c;软件IT行业面临着前所未有的数据安全挑战。黑客攻击、病毒传播、内部泄密等安全威胁层出不穷&#xff0c;给企业的核心资产和运营带来严重威胁。同时&#xff0c;国家对于数据安全的法律法规也日益严格&#xff0c;要求企业必须采取更加有效…

【转载】MyBatisCodeHelperPro最新版使用教程

在开发中编写生成bean&#xff0c;mapper&#xff0c;mapper.xml费时也费力&#xff0c;可以通过MyBatisCodeHelper-Pro自动生成bean&#xff0c;dao&#xff0c;mapper.xml等文件。 MyBatisCodeHelper-Pro是IDEA下的一个插件&#xff0c;类似于mybatis plugin&#xff0c;但可…

iPhone是国内最畅销的智能手机

据调研机构BCI发布最新数据显示&#xff0c;去年中国一共卖出2.7亿部智能手机&#xff0c;其中&#xff0c;苹果的iPhone系列是国内最畅销的机型。 其中&#xff0c;苹果以17.1%的市场份额占据了第一&#xff0c;而vivo手机和OPPO则以16.7%和16%紧随其后&#xff0c;接着是荣耀…

【C#】面向对象的三大特性,还记得吗,简单代码举例回顾

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《C#》序列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握。…

论文阅读 Vision Transformer - VIT

文章目录 1 摘要1.1 核心 2 模型架构2.1 概览2.2 对应CV的特定修改和相关理解 3 代码4 总结 1 摘要 1.1 核心 通过将图像切成patch线形层编码成token特征编码的方法&#xff0c;用transformer的encoder来做图像分类 2 模型架构 2.1 概览 2.2 对应CV的特定修改和相关理解 解…

程序员应该学习的 10 件事

程序员应该学习的 10 件事&#xff08;省流版&#xff09; 翻译&#xff1a;10 Things Software Developers Should Learn about Learning 原文&#xff1a;https://cacm.acm.org/magazines/2024/1/278891-10-things-software-developers-should-learn-about-learning/fulltex…

【教学类-43-21】完结篇 16宫格(4*4可算全部数字)

作品展示&#xff1a; 16宫格里面的4*4小格子可以凑满1-16&#xff0c;旁边的7宫格格2份 背景需求&#xff1a; 做完了1-20宫格的A4模板&#xff0c;最后做一个16宫格小格子&#xff08;附加7宫格2套&#xff09;的样式&#xff0c;只有4宫格&#xff08;2*2&#xff09;、9宫…

vue3二次封装element-ui中的table组件

为什么要做这件事 借助封装table组件的过程来巩固一下vue3相关知识点。 组件有哪些配置项 options:表格的配置项data: 表格数据源elementLoadingText&#xff1a;加载文案elementLoadingSpinner&#xff1a;加载图标elementLoadingBackground&#xff1a;背景遮罩的颜色elem…

【RabbitMQ】RabbitMQ高级:死信队列和延迟队列

目录 设置TTL&#xff08;过期时间&#xff09;概述RabbitMQ使用TTL原生API案例springboot案例 死信队列概述原生API案例springboot案例 延迟队列概述插件实现延迟队列安装插件代码 TTL实现延迟队列实现延迟队列优化 设置TTL&#xff08;过期时间&#xff09; 概述 在电商平台…

Netty开篇——NIO章下(五)

SelectionKey 表示 Selector 和网络通道的注册关系&#xff0c;共四种(全是常量): Int OP_ACCEPT:有新的网络连接可以接受&#xff0c;值为 16 &#xff08;1 << 4&#xff09;Int OP_CONNECT: 代表连接已经建立&#xff0c;值为 8 &#xff08;1 << 3&#xff09;…