背景
今天周六,在家无聊。准备研究一个以前自己半懂不懂的问题,这个utf8和unicode究竟是啥?查了一下午的网上资料,给我脑袋看炸了。什么乱七八糟的,有的说的很有道理,有的越说越糊涂,好像多么高深似的,非要把人弄的兴趣全无。为了解释一个东西,就新造一个新的术语。简直是xxx。
好了,吐槽完毕,言归正传,为了帮助大家简单且深刻的理解一下utf8和unicode,我下面将争取用最朴实的语言来描述,有不到位的地方,希望大家多多评论,一起学习,多谢各位。
- 示例语言:go
- 版本:1.18
哎,怎么不一样?
咱想看一下 “韭” 这个字中内存怎么存的。代码如下
var s = "韭"
for i:=0;i<len(s);i++{
// %x 表示输出16进制
fmt.Printf("0x%x ",s[i])
}
输出结果为:
0xe9 0x9f 0xad
不都说,go是直接用utf8编码的吗?我这输出的应该就是"韭"字的utf8编码吧。去找个在线转换软件试试。
试试情况如下:
好奇怪,这怎么不一样,一个是0xe9 0x9f 0xad
,一个是韭
。这在线转换工具,转换出了个啥啊这是?怎么不一样啊。
原来是它
经过查阅多方资料,上面转换出来的韭
其实是unicode字符。对于unicode,我一直是熟悉且陌生的,很惭愧,干编程工作这么多年。这个最基本的还没完全弄懂。
unicode
为啥会存在这个unicode
计算机专家想把全世界的语言文字都能够在计算机中显示出来。你不可能直接简单的把"韭"字直接塞进计算机吧。对吧,得找个数字来表示"韭"字。
就类似于什么?你在学校里面的学号。比如,张三学号是38号。就是这么个意思,这个数字38就代表张三,就是张三在学校里面的unicode码。
因为全世界的文字是非常多的,有英文,中文,日文,韩文。你看看,单单东亚及这么多文字。
你不可能一个地方一个标准,比如1在亚洲表示中文"我"字,在非洲表示另外一种文字,那不就彻底乱套了。
所以,这个时候统一的标准就来了,这就是unicode。在这套标准下面。假设,"韭"字的学号在北极的电脑是38号,那么在南极也是38号。全球统一。
utf8
我们上面搞明白了unicode,那么这个utf8又是个啥玩意,我不是已经解决了全球各种文字在计算机中存储形式了吗?
这么想是有道理的。
但是我们再回过头来看看,unicode保存了全球的文字,那就有一个什么问题?忒大了。
英文字母也就26个,但是汉字多少个,反正几万个是有的,常用的也有几千个。英文字母26个,一个字节完全能够放的下,但是1个字节存汉字肯定不够,因为一个字节是8个bit位,最多能表示255种数。
查阅资料得知,有的文字在unicode甚至用到了4个字节长度的数。
那要是直接把unicode直接存内存,都用4个字节存,对于有些文字来说就非常浪费了,完全用不上。
所以,这个时候,utf8就诞生了,utf8长度是可变的。
小结
到此为止,我们终于搞清楚了utf8和unicode的对应关系。
unicode是文字的学号(专业一点叫码点,但是此处不想再引入新的名词,浪费时间,浪费兴趣,毫无意义,大家了解即可),所以每一个文字在unicode中都唯一对应一个学号。
utf8是文字实际存储在内存中的样子。
相关go代码
对于上面一开始的问题,其实是网站的问题,网站所谓的utf8其实是unicode编码。
func main() {
var s = "韭"
for i := 0; i < len(s); i++ {
// 这里打印的utf8编码
fmt.Printf("0x%x ", s[i])
}
fmt.Println()
for _, i := range s {
// 这里打印的是unicode
fmt.Printf("0x%x ", i)
}
fmt.Println()
encodeRune()
decodeRune()
}
// rune -> []byte
func encodeRune() {
// "韭"字的unicode编码
var r rune = 0x97ed
fmt.Printf("the unicode charactor is %c\n", r) // 中
buf := make([]byte, 3)
// 编码成utf8
_ = utf8.EncodeRune(buf, r) // 对rune进行utf-8编码
fmt.Printf("utf-8 representation is 0x%X\n", buf) //
}
// []byte -> rune
func decodeRune() {
// utf8
var buf = []byte{0xe9, 0x9f, 0xad}
// 解码成unicode
r, _ := utf8.DecodeRune(buf) // 对buf进行utf-8解码
fmt.Printf("the unicode charactor after decoding [0xe9, 0x9f, 0xad] is %s\n", string(r)) // 中
}