您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

彻底搞懂字符编码

时间:2021-12-14 09:59:43  来源:  作者:前端晚间课

此文由前美团前端工程师@小鱼儿授权发布

背景

在日常开发中很少接触到字符的概念,大部分语言对字符的转换都已经封装的足够好,不需要开发人员过多考虑编码解码的问题。但是字符编码又经常在开发中遇见,所以这一篇文章就是解决,到底什么是字符集以及其编码。举个很简单的例子,调用字符串的length函数,其中的英文,汉字,emoji,长度分别是多少?length长度是如何计算的?IOS中的NSString,JAVAscript中的String在内存中的编码方式是什么?搞懂了字符编码,这些问题就迎刃而解了。

编码、ASCII

我们知道计算机只处理0和1,所有可见的文件,视频,音频等都是以二进制的形式存储和运算的。我们用八个二进制位表示一个字节,那么一个字节就可以代表256种“字符”。举个例子,字母“A”定义为65,用二进制表示是0100 0001。

彻底搞懂字符编码

 

这种把A转换成0100 0001的形式就是一次映射的过程。再来看ASCII码一共有128个字符,那么就有128个映射。一个字节就足足的可以表示了。ASCII就是最早期的字符集。

彻底搞懂字符编码

 

字符集

随着计算机的普及,需要做映射的字符越来越多,光常用汉字就几千个了,这时候ASCII码已经不够用了,涌现了很多字符集,ISO-8859,GB2312,GBK等,直到后来为了解决各个字符集各自为战的问题,分别产生了Unicode 组织和 ISO-10646工作小组,最后这两家组织也合并了,形成现如今的Unicode.

彻底搞懂字符编码

 

Unicode

Unicode是一种计算行业标准,用于对世界上大多数书写系统中表示的文本进行一致的编码,表示和处理。该标准由Unicode联盟维护,截至2019年5月,最新版本Unicode 12.1包含137994个字符的库,涵盖150个现代和历史脚本以及多个符号集和表情符号。Unicode标准的字符库与ISO / IEC 10646同步,并且两者的代码相同。Unicode也是一种字符集。通常会用U+十六进制表示,可存储0000 ~ 10FFFF 共 1114112 个值,2^16(65536)个号码组成一个平面,一共有17个平面,其中第一个0号平面占了绝大部分常用的字符。看下图可以比较直观的了解,其中每个最小的格子是一个字节即代表256个编码点,每个大格子有65536个编码点,蓝色区域是已被使用的区域,绿色是自用区,红色区域是代理区。

彻底搞懂字符编码

 

再将前三个格子放大,蓝绿色部分是汉字,棕色部分是朝鲜语,由这两张图可以最直观的了解Unicode存储空间。Unicode的实现方式有多种,其中最常用的是UTF-8和UTF-16,下面逐一介绍两个实现原理。

彻底搞懂字符编码

 

UTF-8

UTF-8是目前使用最广的Unicode编码方式,它是一种可变长的编码方式,从1个字节到4个字节不等。下图是它的编码规则:

1.对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

2.对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。

彻底搞懂字符编码

 

根据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。

下面,还是以汉字“严”为例,演示如何实现 UTF-8 编码。

“严”的 Unicode 是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800 - 0000 FFFF),因此“严”的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,从“严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,“严”的 UTF-8 编码是11100100 10111000 10100101,转换成十六进制就是E4B8A5。

 

优点:

1.兼容ASCII.

2.没有字节序问题。(后面会讲到字节序)

3.对于英文编码较短,占用空间小。

4.可变长,空间足够大

5.容错性好,中间丢失字节,后面的字节还是可以根据编码规则解码,不影响后面的字符生成。

 

缺点:

1.对于中日韩的语言,一个字符需要三个字节表示,占用空间大。

2.计算长度效率低,由于是变长的,所以在计算字符串长度的时候执行效率比较低。

 

UTF-16

UTF-16也是经常用到的编码方式,同样它也是可变长的编码方式,下图是编码规则,字符长度2个字节或者4个字节表示一个字符。

彻底搞懂字符编码

 

随即就有了一个问题,当我们遇到两个字节,怎么看出它本身是一个字符,还是需要跟其他两个字节放在一起解读形成一个字符?

U+D800到U+DFFF是一个空段,这些码点不对应任何字符,编号大于U+0FFFF的字符,一半在U+D800到U+DBFF之间,一半在U+DC00到U+DFFF之间,当我们遇到两个字节,发现它的码点在U+D800到U+DBFF之间,就可以断定,紧跟在后面的两个字节的码点,应该在U+DC00到U+DFFF之间,这四个字节必须放在一起形成一个字节。刚好可以涵盖辅助面的字符。

 

优点:

1.由于是固定2个字节和4个字节,所以在计算字符串长度、执行索引操作时速度很快。

缺点:

1.UTF-16 能表示的字符数有 6 万多,但是实际上目前 Unicode 5.0 收录的字符已经达到 99024 个字符,早已超过 UTF-16 的存储范围。

2.UTF-16 存在字节序问题(大端和小端)使用时需要提前协定好。

3.容错性差,因为存在字节序的问题,如果中间某一个字节丢失时可能会导致后面的解码错误。

UTF-32

4 个字节表示一个代码值,固定长度,多出来的部分前面补0,这种编码方式占空间较多,使用场景很少。

字节序

字节序是指数据在存储器中的存放顺序,分为大端和小端两种。之所以会存在字节序的问题是因为寄存器的长度要大于一个字节,不同的操作系统读取字节的顺序不一样,

大端模式,是指数据的高字节在前,保存在内存的低地址中,与人类的读写法一致,数据的低字节在后,保存在内存的高地址中,文件前缀FE FF。(mac OS是大端模式)

小端模式,是指数据的高字节在后,保存在内存的高地址中,而数据的低字节在前,保存在内存的低地址中,文件前缀FF FE。(x86和一般的OS(如windows,FreeBSD,linux)使用的是小端模式)。

彻底搞懂字符编码

 

所以UTF-16存在一个字节序的问题,需要在文件前面声明,而UTF-8不存在这个问题,原因在于UTF-8的最小编码单位是1个字节,不会存在两个字节谁在高位谁在地位的问题。

还是以汉字“严”为例,Unicode 码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,这就是 Big endian 方式(4E 25)。25在前,4E在后,这是 Little endian 方式(25 4E)。

 

用途

UTF-8,广泛用于数据存储及传输:例如html文档中的<meta charset="UTF-8">,以及Python文件当出现中文的时候会在顶部加上“# coding: UTF-8”等

UTF-16,而一些流行语言比如Java、JavaScript、Python、Objective-C等字符串内部字符串都用UTF-16编码.在计算字符串长度搜索是的效率较好。

 

Objective-C中的NSString

彻底搞懂字符编码

 

Java中的String类

彻底搞懂字符编码

 

实践

1.日常字母,汉字,表情分别用UTF-8UTF-16表示分别用多少字节?

字母UTF-8用一个字节,UTF-16用两个字节。

大部分的汉字UTF-8编码后由三个字节如下图的“严”是e4b8a5,而用UTF-16编码仅用2个字节即4e25。

大部分表情等特殊符号UTF-8编码后占4个字节,UTF-16编码后也占4个字节。

彻底搞懂字符编码

 

2.字符计算长度length方法是怎么计算的?

像NSString,java,javascript等语言,由于是UTF-16编码的,在计算长度的时候由总字节/2得来的。

3.实际开发时,如果想计算字符实际长度该怎么计算?

1)一种是通过判断码点所在位置进行判断,只要落在0xD800到0xDBFF的区间,就要连同后面2个字节一起读取.

var index = -1;
var string = '12';
var length = string.length;
var output = [];
while (++index < length) {
  var charCode = string.charCodeAt(index);
  var character = string.charAt(index);
  if (charCode >= 55296 && charCode <= 56319) {
    output.push(character + string.charAt(++index));
  } else {
    output.push(character);
  }
}
console.log(output) //["", "1", "2"]
consolo.log(0xD800 ===55296) // true

2.)ECMAScript 6版本 增强了对Unicode的支持,基本解决了这个问题。

let s = '12';
let output = [];
for(let s of string ){ 
    output.push(s)
}
console.log(output) //["", "1", "2"]

Array.from(string).length

4.javascript字符串和码点之间的转换方法?

  • String.fromCodePoint():从Unicode码点返回对应字符
console.log(String.fromCodePoint(9731, 9733, 9842, 0x2F804));
// expected output: "☃★♲你"

  • String.prototype.codePointAt():从字符返回对应的码点
var icons = '☃★♲';
console.log(icons.codePointAt(1));
// expected output: "9733"

5.文件编码是UTF-8,和字符串UTF-16编码之间有什么关系?

这里以python为例,我们都知道,磁盘上的文件都是以二进制格式存放的,其中文本文件都是以某种特定编码的字节形式存放的。对于程序源代码文件的字符编码是由编辑器指定的,比如我们使用Pycharm来编写Python程序时会指定工程编码和文件编码为UTF-8,那么Python代码被保存到磁盘时就会被转换为UTF-8编码对应的字节(encode过程)后写入磁盘。当执行Python代码文件中的代码时,Python解释器在读取Python代码文件中的字节串之后,需要将其转换为Unicode字符串(decode过程)之后才执行后续操作。

彻底搞懂字符编码

 

总结

上面讲了Unicode字符集的起源,它的三种编码方式,UTF-8,UTF-16,UTF-32,编码空间,编码规则,优缺点以及用途,之后结合实际场景应用介绍了js中一些实践方法,不同语言会有自己的方式,还有就是文件编码和执行时字符编码。这些知识点在实际开发中不常见,但是深入了解其原理,对日常开发和解决问题会有帮助。

 

http://reedbeta.com/blog/programmers-intro-to-unicode/

https://zh.wikipedia.org/zh-cn/Unicode#%E6%A8%99%E6%BA%96

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

http://www.ruanyifeng.com/blog/2014/12/unicode.html

https://www.cnblogs.com/yyds/p/6171340.html



Tags:字符编码   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
此文由前美团前端工程师@小鱼儿授权发布背景在日常开发中很少接触到字符的概念,大部分语言对字符的转换都已经封装的足够好,不需要开发人员过多考虑编码解码的问题。但是字符...【详细内容】
2021-12-14  Tags: 字符编码  点击:(25)  评论:(0)  加入收藏
1 JAVA.IO字节流 inputstream.png LineNumberInputStream和StringBufferInputStream官方建议不再使用,推荐使用LineNumberReader和StringReader代替 ByteArrayInputStream和B...【详细内容】
2020-08-13  Tags: 字符编码  点击:(42)  评论:(0)  加入收藏
文件流、字节流、字符流、缓冲流、转换流、内存流文件流: 在Java 中,文件流负责操作文件,包括读取和写入;FileInputStream // 文件的字节输入流;FileOutputStream //...【详细内容】
2020-05-03  Tags: 字符编码  点击:(44)  评论:(0)  加入收藏
要说在整个编程领域中最难的问题有哪些的话,字符编码的问题,也就是乱码问题,绝对算得上很多程序员写代码时的一个“噩梦”。以至于在IT界有个著名的笑话,“手持一把锟斤拷,口中直...【详细内容】
2019-10-09  Tags: 字符编码  点击:(113)  评论:(0)  加入收藏
▌简易百科推荐
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(1)  评论:(0)  加入收藏
程序是如何被执行的&emsp;&emsp;程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(9)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(19)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(23)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(24)  评论:(0)  加入收藏
一个项目的大部分API,测试用例在参数和参数值等信息会有很多相似的地方。我们可以复制API,复制用例来快速生成,然后做细微调整既可以满足我们的测试需求1.复制API:在菜单发布单...【详细内容】
2021-12-14  AutoMeter    Tags:AutoMeter   点击:(20)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条