博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 面试准备 4 UTF-8 和 Unicode 解码、编码
阅读量:2209 次
发布时间:2019-05-04

本文共 4351 字,大约阅读时间需要 14 分钟。

首先明确一点:

UFT-8是编码方式

Unicode是字符集


U n i c o d e \color{red}{Unicode} Unicode

字符集,如ASCII字符集

97—‘a’

98—‘b’

Unicode 则是

U+2074—‘4’

U+2075—‘5’

Unicode 表示的范围:U+0000 to U+10FFFF (十六进制)

2^21=xxx 我就不具体算了,但足以看出和ASCII完全不是一个量级的

原本Unicode 就是为了解决ASCII 不够用的情况而发明的

(全球化,中文,日文,等等 Unicode—Universal )

但实际上,Unicode 出现了“性能过剩”的情况-----Unicode码还有挺多空着没用的

U n i c o d e 中 还 有 字 符 来 表 示 易 经 中 的 八 卦 符 号 。 。 。 \color{blue}{Unicode 中还有字符来表示易经中的八卦符号。。。} Unicode

尽管如此,还是有空着的Unicode码!牛逼牛逼~

大家可以到这个网站去看看

表示的多了,需要的空间也就多

完整覆盖unicode 需要4字节的数据

而ASCII码只需要1字节

我们需要一个折中的办法~

然后就有了像UTF-8 这样的编码方式来合理、高效地使用Unicode 码


字 符 集 与 编 程 语 言 \color{red}{字符集与编程语言}

科学家发现,用2字节的Unicode 就足够全球的用户日常使用了~

2字节,16位,2^16=65536

别觉着少,前面说了,是日常使用,我们中国日常使用的汉字都大概只有3500个左右

如此一看60000真的够了

Java 里的char类型就是2字节,原因就是采用了Unicode的字符集

C语言中char 占1字节,采用的是ASCII的字符集,有0~127 共128个字符

你可能会问,为什么1字节,2^8=256,为啥表示范围只有0~127

因为。。这C语言美国人发明的,对他们来说,128个已经足够表示所有英文字母和很多特殊符号了

(当然,后来有了拓展的ASCII,可以参考这篇文章:)


U T F − 8 \color{red}{UTF-8} UTF8

首先,UTF-8 可变字节,可以用1字节表示,也可以用2字节表示,还可以3字节,4字节

UTF-8:

1 byte: Standard ASCII (标准ASCII)
2 bytes: Arabic, Hebrew, most European scripts (most notably excluding Georgian)(多国的语言)
3 bytes: BMP(Basic Multilingual Plane 基本多文种平面)
4 bytes: All Unicode characters(所有Unicode 字符)

B M P \color{blue}{BMP} BMP

上面还需要补充介绍的就是BMP(Basic Multilingual Plane 基本多文种平面)了

前面提到过,Unicode 的范围:U+0000 to U+10FFFF (16进制)

10FFFF 前面的 “10” 是用来标识平面(Plane)的

十六进制的“10”转化成十进制就是16

0~16,则Unicode 总共有17个Plane(平面)

而BMP 就是第一个编号为0的平面

这个平面上的字符,就是我们上面提及的足够我们日常使用的一个平面

(一个平面Plane有两字节的空间-----联想一下Java的2字节char)


有人可能会问了,UTF-8 是怎么实现可变字节数的呢?

举个例子:

\xe2\x81\xb4

我怎么知道上面这UTF-8的字符串到底表示一个Unicode还是两个,还是三个?

这就涉及到UTF-8 的编码规则了

编 码 规 则 \color{red}{编码规则}

(二进制,8位为一字节)

一个字节的UTF-8(0开头): 0xxxxxxx

两个字节的UTF-8(开头两个1加0,另一字节开头是10):110xxxxx 10xxxxxx

三个字节的UTF-8(开头三个1加0,另两字节开头也是10):1110xxxx 10xxxxxx 10xxxxxx

四个字节的UTF-8(开头四个1加0,另三字节开头也是10):11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

现在回到刚刚的字符串

\xe2\x81\xb4

把十六进制转到二进制

11100010 10000001 11000100

可以看到在第一个字节中,开头是三个1加0

则表示这是三个字节的UTF-8

则 “\xe2\x81\xb4” 一起只表示一个Unicode码

具体对应哪个Unicode吗呢?

再接着看

U T F − 8 到 U n i c o d e , 解 码 \color{red}{UTF-8到Unicode,解码} UTF8Unicode

把转化出来的二进制串对应编码规则中的模板

11100010 10000001 101101001110xxxx 10xxxxxx 10xxxxxx

xxxx对应上去,我们得到:

1110xxxx 10xxxxxx 10xxxxxx    0010   000001   110100

把上面得到的新串每四位为一个单元分开则有:

0010 0000 0111 0100

转化成16进制:2074

则 “\xe2\x81\xb4” 对应的Unicode码为 U+2074

具体表示什么字符,就可以查表了~

上 面 介 绍 的 就 是 所 谓 解 码 d e c o d e ( ) 的 操 作 \color{blue}{上面介绍的就是所谓解码decode()的操作} decode()


U n i c o d e 到 U T F − 8 , 编 码 \color{red}{Unicode到UTF-8,编码} UnicodeUTF8

进行编码时,对Unicode码也有一套规则

0x00000000 - 0x0000007F:

0xxxxxxx
0x00000080 - 0x000007FF:
110xxxxx 10xxxxxx
0x00000800 - 0x0000FFFF:
1110xxxx 10xxxxxx 10xxxxxx
0x00010000 - 0x001FFFFF:
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

不同范围的Unicode 码,对应不同字节的UTF-8

像刚刚的U+2074,范围就在 0x00000800 - 0x0000FFFF

则对应3字节的UTF-8,知道使用哪个模板后

把Unicode 码转化成2进制,然后填进xxxx的位置就好了~

(就把解码的工作逆着来一遍就好了)


U T F − 8 与 U T F − 16 , U T F − 32 \color{red}{UTF-8与UTF-16,UTF-32} UTF8UTF16UTF32

UTF-8 之所以为8,是因为它以8比特(一字节)为一个基本单元

UTF-16 则是以16比特(2字节)为一个基本单元

UTF-32 同理

对于UTF-16

和UTF-8 一样,也有一套编码规则:

两字节的UTF-16:xxxxxxxxxxxxxxxx

四字节的UTF-16:110110xxxxxxxxxx 110111xxxxxxxxxx

Unicode:

范围1.U+00000000 - U+0000D7FF xxxxxxxxxxxxxxxx

范围2.U+0000E000 - U+0000FFFF xxxxxxxxxxxxxxxx

范围3.U+00010000 - U+0010FFFF 110110xxxxxxxxxx 110111xxxxxxxxxx

细心的朋友可能会发现,上面Unicode 的范围1和范围2并不是连续的

D7FF转到二进制是1101 1111 1111 1111

没错

正好对应范围三中的 110110xxxxxxxxxx

这也就是UTF-16 实现可变字节的手段~

(这里我就不细说了,大家稍微动动脑筋就明白为什么空开一段就能让UTF-16实现变字节)

需要补充的是空开的那一段,U+D800 - U+DFFF

维基百科上是这么描述的():

The Unicode standard permanently reserves these code point values for UTF-16

encoding of the high and low surrogates, and they will never be assigned a character,
so there should be no reason to encode them.
The official Unicode standard says that no UTF forms, including UTF-16, can encode these code points.

大意就是说那一段的Unicode 就是为UTF-16空着的,不会设置有效字符

所以大家就不用顾虑啦~

其实那一段空着并不是浪费的,详细的我这也不展开了

有兴趣了解的朋友,就上面那个维基百科的链接有详细介绍
小标题是:Code points from U+010000 to U+10FFFF
大意就是UTF-16把那空着的段又分成了两段:
0xD800–0xDBFF(high surrogate) 和 0xDC00–0xDFFF(low surrogate)
分别对应UTF-16四字节的规则中的前两字节和后两字节
两两组合出来了UTF-16后面绝大部分的Unicode:U+00010000 - U+0010FFFF

UTF-32

这就没有什么可变字节了

直接32位四字节硬怼


总结:

Unicode 是为了应对 ASCII字符集不够用而发明的

而直接用4字节去表示Unicode 又容易浪费资源

所以有了UTF-8、UTF-16 这样的编码方式


对了还得补充一点

对于亚洲的一些文字,例如说我们的中文

用UTF-8需要3字节

而用UTF-16则只需要2字节

但是,互联网中普遍都是用UTF-8

因为,互联网的语言是英文。。。

转载地址:http://roiyb.baihongyu.com/

你可能感兴趣的文章
【LEETCODE】228-Summary Ranges
查看>>
【LEETCODE】27-Remove Element
查看>>
【LEETCODE】66-Plus One
查看>>
【LEETCODE】26-Remove Duplicates from Sorted Array
查看>>
【LEETCODE】118-Pascal's Triangle
查看>>
【LEETCODE】119-Pascal's Triangle II
查看>>
【LEETCODE】88-Merge Sorted Array
查看>>
【LEETCODE】19-Remove Nth Node From End of List
查看>>
【LEETCODE】125-Valid Palindrome
查看>>
【LEETCODE】28-Implement strStr()
查看>>
【LEETCODE】6-ZigZag Conversion
查看>>
【LEETCODE】8-String to Integer (atoi)
查看>>
【LEETCODE】14-Longest Common Prefix
查看>>
【LEETCODE】38-Count and Say
查看>>
【LEETCODE】278-First Bad Version
查看>>
【LEETCODE】303-Range Sum Query - Immutable
查看>>
【LEETCODE】21-Merge Two Sorted Lists
查看>>
【LEETCODE】231-Power of Two
查看>>
【LEETCODE】172-Factorial Trailing Zeroes
查看>>
【LEETCODE】112-Path Sum
查看>>