UTF-8 与 UTF-16编码详解_utf-8和utf-16-程序员宅基地

技术标签: Java基础  UTF-16  

目录

一、UTF-8编码

1、UTF-8介绍

2、UTF-8是如何编码的?

3、上述Unicode码点值范围中十进制值127、2047、65535、2097151这几个临界值是怎么来的呢?

二、UTF-16编码

1、UTF-16介绍

2、UTF-16编码方式

1)设计思路

2)具体编码方式

3)字节顺序问题

3、BOM

三、两者比较

1、存储容量

2、存储效率

3、字节序


一、UTF-8编码

1、UTF-8介绍

UTF-8编码是Unicode字符集的一种编码方式(CEF),其特点是使用变长字节数(即变长码元序列、变宽码元序列)来编码。一般是1到4个字节,当然,也可以更长。

为什么要设计为变长字节数?

可以理解为按需分配,比如一个字节足以容纳所有的ASCII字符,那何必补一堆0用更多的字节来存储呢?

变长编码的优势与劣势

优势是节省空间、自动纠错性能好、利于传输、扩展性强,劣势是不利于程序内部处理,比如正则表达式检索;而UTF-32这样等长码元序列(即等宽码元序列)的编码方式就比较适合程序处理,当然,缺点是比较耗费存储空间。

2、UTF-8是如何编码的?

UTF-8编码最短的为一个字节、最长的目前为四个字节,从首字节就可以判断一个UTF-8编码有几个字节:

  • 如果首字节以0开头,肯定是单字节编码(即单个单字节码元);
  • 如果首字节以110开头,肯定是双字节编码(即由两个单字节码元所组成的双码元序列);
  • 如果首字节以1110开头,肯定是三字节编码(即由三个单字节码元所组成的三码元序列),以此类推。

另外,UTF-8编码中,除了单字节编码外,由多个单字节码元所组成的多字节编码其首字节以外的后续字节均以10开头(以区别于单字节编码以及多字节编码的首字节)。

0、110、1110以及10相当于UTF-8编码中各个字节的前缀,因此称之为前缀码。其中,前缀码110、1110及10中的0,是前缀码中的终结标志。

UTF-8编码中的前缀码起到了很好的区分和标识的作用——当解码程序读取到一个字节的首位为0,表示这是一个单字节编码的ASCII字符;当读取到一个字节的首位为1,表示这是一个非ASCII字符的多字节编码字符中的某个字节(可能是首字节,也可能是后续字节),接下来若继续读取到一个1,则确定为首字节,再继续读取直到遇见终结标志0为止,读取了几个1,就表示该字符为几个字节的编码;当读取到一个字节的首位为1,紧接着读取到一个终结标志0,则该字节显然是非ASCII字符的后续字节(即非首字节)。

所以,1~4字节的UTF-8编码看起来分别是这样的:

  • 单字节可编码的Unicode码点值范围十六进制为0x0000 ~ 0x007F,十进制为0 ~ 127;
  • 双字节可编码的Unicode码点值范围十六进制为0x0080 ~ 0x07FF,十进制为128 ~ 2047;
  • 三字节可编码的Unicode码点值范围十六进制为0x0800 ~ 0xFFFF,十进制为2048 ~ 65535;
  • 四字节可编码的Unicode码点值范围十六进制为0x10000 ~ 0x1FFFFF,十进制为65536 ~ 2097151

目前Unicode字符集码点编号的最大值为0x10FFFF,实际尚未编号到0x1FFFFF;这说明作为变长字节数的UTF-8编码其未来扩展性非常强,即便目前的四字节编码也还有大量编码空间未被使用,更不论还可扩展为五字节、六字节…...。

3、上述Unicode码点值范围中十进制值127、2047、65535、2097151这几个临界值是怎么来的呢?

因为UTF-8编码中的每个字节中都含有起到区分和标识之用的前缀码0、110、1110以及10之一,所以1~4个字节的UTF-8编码其实际有效位数分别为8-1=7位(2^7-1=127)、16-5=11位(2^11-1=2047)、24-8=16位(2^16-1=65535)、32-11=21位(2^21-1=2097151),如下表所示:

注:上图中的Unicode range即Unicode码点值范围(也就是Unicode码点编号范围),Hex为16进制,Binary为二进制;Encoded bytes即UTF-8编码中各字节的编码方式(即编码算法),其中,x代表Unicode二进制码点值的单字节或低字节中的低7位或8位、y代表两字节码点值的高字节中的低3位或8位以及三字节码点值的中字节中的8位、z代表三字节码点值的高字节中的低5位。

因此,UTF-8编码的算法简单地用一句话来概括就是:首先确定UTF-8编码中各个字节的前缀码;之后再将UTF-8编码中各个字节除了前缀码所占用之外的位,依次分配给Unicode字符码点值二进制中各个位的值,换言之,就是用Unicode字符码点值二进制中各个位的值,依次填充UTF-8编码中的各个字节除了前缀码所占用之外的位。

参考文章:刨根究底字符编码之十二——UTF-8究竟是怎么编码的 - 腾讯云开发者社区-腾讯云 (tencent.com)

二、UTF-16编码

1、UTF-16介绍

UTF-16是Unicode字符编码五层次模型的第三层:字符编码表(Character Encoding Form,也称为 "storage format")的一种实现方式。即把Unicode字符集的抽象码位映射为16位长的整数(即码元, 长度为2 Byte)的序列,用于数据存储或传递。Unicode字符的码位,需要1个或者2个16位长的码元来表示,因此这是一个变长表示。

引用维基百科中对于UTF-16编码的解释我们可以知道,UTF-16最少也会用2 Byte来表示一个字符,因此没有办法兼容ASCII编码(ASCII编码使用1 Byte来进行存储)。 

2、UTF-16编码方式

1)设计思路

我们知道Unicode的范围为0x0~0x10FFFF,首先是0x0~0xFFFF这段区间,正好16位就可以表示,那么超过这个区间的怎么办呢?也就是0xFFFF~0x10FFFF这段,我们先看这段区间有多少个码位,0x10FFFF-0xFFFF=0x100000,那么这个十六进制表示的十进制也就是:1048576个码位

我们既然16位存不下,那肯定就是32位存咯,将32位分开前16位和后16位,每个16位各存一半,那么每一半存的就是1024(由来:1024*1024=1048576),1024代表的是2的10次幂,也就是10位二进制数。

32位二进制数字中,前后16位中各存10位就够用了,但是剩余的6位用来干什么呢?和UTF-8的设计一样,为了让识别字符串变得容易(从文本的任意位置开始,均能区分一个字符的起始)。

我们通过前6位来区分数据,那么前6位就是2^6=64,也就是开头数字的区间。我们设定如下:
54开头的为32位的前16位,55开头的为32位的后16位,其他开头的为单16位,这样我们就能区分开这三个16位了,在读取文档中的任意位置,都能随意区分出间隔咯。

  • 那么54开头的数据区间是多少呢,就是1101 10xx xxxx xxxx,区间就是D800~DBFF
  • 那么55开头的数据区间是多少呢,就是1101 11xx xxxx xxxx,区间就是DC00~DFFF

为了配合UTF-16,Unicode中也将这两个区间屏蔽掉,不允许分配任何字符,这个区间就是代理区。

2)具体编码方式

在UTF-16中,我们将Unicode分为了两个范围,分别通过不同的方式进行存储。具体表示见下图。

Unicode范围

UTF-16编码方式

U+000~U+FFFF

2 Byte存储,编码后等于Unicode值

U+10000~U+10FFFF

4 Byte存储,现将Unicode值减去(0x10000),得到20bit长的值。再将Unicode分为高10位和低10位。UTF-16编码的高位是2 Byte,高10位Unicode范围为0-0x3FF,将Unicode值加上0XD800,得到高位代理(或称为前导代理,存储高位);低位也是2 Byte,低十位Unicode范围一样为0~0x3FF,将Unicode值加上0xDC00,得到低位代理(或称为后尾代理,存储低位)

  • 0x3FF  -->  0011 1111 1111 
  • 0xD800  -->  1101 1000 0000 0000
  • 0xDC00  -->  1101 1100 0000 0000

3)字节顺序问题

由于一开始的Unicode只需要两个字节,所以UTF-16虽然也是变长编码方式,但是在最初却可以当做定长编码方式使用。UTF-16每个字符都直接使用两个字节存储,所以就有字节顺序的问题,同一字节流可能会被解释为不同内容。如某字符为十六进制编码4E59,按两个字节拆分为4E59,在Mac中和Windows中会解析如下:

- 读取顺序 显示字符
Windows 4E 59
Mac 59 4E

在Mac上从低字节开始和在Windows上从高字节开始读取显示不同,从而导致在同一编码下的乱码问题。为了解决这个问题便引入了字节顺序标记(英语:byte-order mark,BOM)来标记是大端序还是小端序。

3、BOM

字节顺序标记(英语:byte-order mark,BOM)是一个有特殊含义的统一码字符,码点为U+FEFF。当以UTF-16或UTF-32来将UCS/统一码字符所组成的字符串编码时,这个字符被用来标示其字节序。经常被用于区分是否为UTF编码。

字符U+FEFF如果出现在字节流的开头,则用来标识该字节流的字节序,是高位在前还是低位在前。如果它出现在字节流的中间,则表达零宽度非换行空格的意义,用户看起来就是一个空格。从Unicode3.2开始,U+FEFF只能出现在字节流的开头,只能用于标识字节序,就如它的名称——字节序标记——所表示的一样;除此以外的用法已被舍弃。取而代之的是,使用U+2060来表达零宽度无断空白。

UTF-8以字节为编码单元,没有字节序的问题。但是某些操作系统也会使用带BOM的UTF-8,叫做UTF-8 with BOM。Python中叫utf-8-sig。Unicode规范中说明UTF-8不必也不推荐使用BOM。多数时候UTF-8都是不带BOM的,但是微软公司的某些软件(如Excel)打开某些不带BOM的utf8文件(如cvs文件)会乱码,需要转换成带BOM的utf8编码才能正常显示。

所以Java中获取以UTF-16编码的字符串字节个数时,总是会比实际含有字符的字节个数多2。不过目前已经有很多主流的文本编辑器支持不带BOM的UTF编码了,通过后缀(LE和BE)区分是小端还是大端。

参考文章:UTF-16编码详解 - 知乎 (zhihu.com)

Unicode中UTF-8与UTF-16编码详解 - 腾讯云开发者社区-腾讯云 (tencent.com)

三、两者比较

1、存储容量

先说UTF-16,由于每个码位都使用2到4个字节来存储,对于含有大量中文或者其他二字节长的字符流来说,UTF-16可以节省大量的存储空间。因为UTF-16并不需要像UTF-8那样通过牺牲很多标记位来标识一个字节表示的是什么,它只需一个字符来表示是大端序和小端序。

但是对于有大量西文字符的字符流来说UTF-8的优势就变得十分明显:UTF-8只需要一个字节就能存储西文字符,这是UTF-16做不到的。所以在混合存储,或者是源代码、字节码文件等大量西文字符的文件,更倾向于UTF-8。

UTF-8存储中文比UTF-16要多出50%,不推荐要大量显示中文的程序使用。—— 知乎轮子哥

而由于UTF-8的兼容性和对西文的支持,所以西方都提倡统一使用UTF-8作为字符编码,这样也的确可以彻底根除乱码问题。目前基本上所有的开发环境和源代码文件也基本上是统一UTF-8。

2、存储效率

这里只从UTF-8和UTF-16两个编码来简单阐述下效率问题。

因为每个字符使用不同数量的字节编码,所以UTF-8编码的字符串,寻找串中第N个字符是一个O(N)复杂度的操作。即串越长,则需要更多的时间来定位特定的字符。同时,还需要位变换来把字符编码成字节,把字节解码成字符。

而从UTF-16编码规则来看,仅仅将字符的高位和地位进行拆分变成两个字节。规则非常简单,编码效率很高,单字节O(1)的查找效率也非常好。

不过值得一提的是,这种时间效率问题正在随着内存和CPU的发展而减小,现在已经不会作为主要考虑的问题了。

3、字节序

UTF-8最大的优势是,没有字节序的概念。所以特别适合用于字符串的网络数据传输,不用考虑大小端问题。对于非英文网页(对于我们而言,简单说东亚文字网页),能够避免各种乱码问题。

UTF-16编码字符串的网络传输,要考虑大小端的问题。另外网络传输中如果一个字节信息丢失,剩下的字符串都无法正确解析,读取混乱,统统乱码,而UTF-8只会影响局部,因为有标识端,后面的数据可以正常读取。

参考文章:字符、编码和Java中的编码 - 简书 (jianshu.com)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_51409098/article/details/126430723

智能推荐

python中文显示不出来_解决Python词云库wordcloud不显示中文的问题-程序员宅基地

文章浏览阅读2.6k次。解决Python词云库wordcloud不显示中文的问题2018-11-25背景:wordcloud是基于Python开发的词云生成库,功能强大使用简单。github地址:https://github.com/amueller/word_cloudwordcloud默认是不支持显示中文的,中文会被显示成方框。安装:安装命令:pip install wordcloud解决:经过测试发现不支持显示中文..._词云python代码无法输出文字

台式计算机cpu允许温度,玩游戏cpu温度多少正常(台式电脑夏季CPU一般温度多少)...-程序员宅基地

文章浏览阅读1.1w次。随着炎热夏季的到来,当玩游戏正爽的时候,电脑突然死机了,自动关机了,是不是有想给主机一脚的冲动呢?这个很大的原因是因为CPU温度过高导致的。很多新手玩家可能都有一个疑虑,cpu温度多少以下正常?有些说是60,有些说是70,到底多高CPU温度不会死机呢?首先我们先看看如何查看CPU的温度。下载鲁大师并安装,运行鲁大师软件,即可进入软件界面,并点击温度管理,即可看到电脑各个硬件的温度。鲁大师一般情况下..._台式机玩游戏温度多少正常

小白自学Python日记 Day2-打印打印打印!_puthon打印任务收获-程序员宅基地

文章浏览阅读243次。Day2-打印打印打印!我终于更新了!(哭腔)一、 最简单的打印最最简单的打印语句: print(“打印内容”)注意:python是全英的,符号记得是半角下面是我写的例子:然后进入power shell ,注意:你需要使用cd来进入你保存的例子的文件夹,保存时名字应该取为xxx.py我终于知道为什么文件夹取名都建议取英文了,因为进入的时候是真的很麻烦!如果你没有进入正确的文件夹..._puthon打印任务收获

Docker安装:Errors during downloading metadata for repository ‘appstream‘:_"cenerrors during download metadata for repository-程序员宅基地

文章浏览阅读1k次。centos8问题参考CentOS 8 EOL如何切换源? - 云服务器 ECS - 阿里云_"cenerrors during download metadata for repository \"appstream"

尚硅谷_谷粒学苑-微服务+全栈在线教育实战项目之旅_基于微服务的在线教育平台尚硅谷-程序员宅基地

文章浏览阅读2.7k次,点赞3次,收藏11次。SpringBoot+Maven+MabatisPlusmaven在新建springboot项目引入RELEASE版本出错maven在新建springboot项目引入RELEASE版本出错maven详解maven就是通过pom.xml中的配置,就能够从仓库获取到想要的jar包。仓库分为:本地仓库、第三方仓库(私服)、中央仓库springframework.boot:spring-boot-starter-parent:2.2.1.RELEASE’ not found若出现jar包下载不了只有两_基于微服务的在线教育平台尚硅谷

随便推点

网络学习第六天(路由器、VLAN)_路由和vlan-程序员宅基地

文章浏览阅读316次。路由的概念路由器它称之为网关设备。路由器就是用于连接不同网络的设备路由器是位于OSI模型的第三层。路由器通过路由决定数据的转发。网关的背景:当时每家计算机厂商,用于交换数据的通信程序(协议)和数据描述格式各不相同。因此,就把用于相互转换这些协议和格式的计算机称为网关。路由器与三层交换器的对比路由协议对比路由器的作用:1.路由寻址2.实现不同网络之间相连的功能3.通过路由决定数据的转发,转发策略称为 路由选择。VLAN相关技术什么是VLAN?中文名称叫:虚拟局域网。虚_路由和vlan

设置div背景颜色透明度,内部元素不透明_div设置透明度,里面的内容不透明-程序员宅基地

文章浏览阅读2.8w次,点赞6次,收藏22次。设置div背景颜色透明度,内部元素不透明:.demo{  background-color:rgba(255,255,255,0.15) } 错误方式:.demo{ background-color:#5CACEE;opacity:0.75;} 这样会导致div里面的元素内容和背景颜色一起变透明只针对谷歌浏览器的测试_div设置透明度,里面的内容不透明

Discuz!代码大全-程序员宅基地

文章浏览阅读563次。1.[ u]文字:在文字的位置可以任意加入您需要的字符,显示为下划线效果。2.[ align=center]文字:在文字的位置可以任意加入您需要的字符,center位置center表示居中,left表示居左,right表示居右。5.[ color=red]文字:输入您的颜色代码,在标签的中间插入文字可以实现文字颜色改变。6.[ SIZE=数字]文字:输入您的字体大小,在标签的中间插入文..._discuzcode 大全

iOS NSTimer定时器-程序员宅基地

文章浏览阅读2.6k次。iOS中定时器有三种,分别是NSTimer、CADisplayLink、dispatch_source,下面就分别对这三种计时器进行说明。一、NSTimerNSTimer这种定时器用的比较多,但是特别需要注意释放问题,如果处理不好很容易引起循环引用问题,造成内存泄漏。1.1 NSTimer的创建NSTimer有两种创建方法。方法一:这种方法虽然创建了NSTimer,但是定时器却没有起作用。这种方式创建的NSTimer,需要加入到NSRunLoop中,有NSRunLoop的驱动才会让定时器跑起来。_ios nstimer

Linux常用命令_ls-lmore-程序员宅基地

文章浏览阅读4.8k次,点赞17次,收藏51次。Linux的命令有几百个,对程序员来说,常用的并不多,考虑各位是初学者,先学习本章节前15个命令就可以了,其它的命令以后用到的时候再学习。1、开机 物理机服务器,按下电源开关,就像windows开机一样。 在VMware中点击“开启此虚拟机”。2、登录 启动完成后,输入用户名和密码,一般情况下,不要用root用户..._ls-lmore

MySQL基础命令_mysql -u user-程序员宅基地

文章浏览阅读4.1k次。1.登录MYSQL系统命令打开DOS命令框shengfen,以管理员的身份运行命令1:mysql -u usernae -p password命令2:mysql -u username -p password -h 需要连接的mysql主机名(localhost本地主机名)或是mysql的ip地址(默认为:127.0.0.1)-P 端口号(默认:3306端口)使用其中任意一个就OK,输入命令后DOS命令框得到mysql>就说明已经进入了mysql系统2. 查看mysql当中的._mysql -u user

推荐文章

热门文章

相关标签