在学习golang的过程中, 发现golang中的字符串可以用切片来访问, 访问的是字节, 而用range来访问, 得到的是字符
于是引申出一个问题: 字节数组怎么字符打印的?
一开始我以为是通过分隔符, 但是最终发现却没有, 是用的编码
那么本文来简单快速总结一下编码吧, 看看unicode, utf-8都是些什么玩意
什么是编码
- 将int与字符一一对应, 就是编码 (个人理解: 给字符编号)
- 键入int, 就能打印出对应的字符
- int用二进制来表示
ascll
- 熟悉的ascll, 美国信息交换标准代码
- ascll是最早的
- 7bit, 128个字符, 表示能力有限
unicode
- 为了解决ascll不够的问题
- 最全
- 32bit, 4个字节, 超过12w字符
- 但是太多, 太大了, 所以一般不用它
utf-8
- 既要全, 又要短(存储空间小)
- 变长编码,
- 使用1到4个字节来表示每个unicode码点, ascll1个字节, 常用字符部分, 2-3个字节, 最长的4个字节
- 每个符号编码后第一个字节的高端bit位用于表示编码总共有多少个字节
utf-8的规则
- 如果第一个字节的高端bit为0,则表示对应7bit的ASCII字符,ASCII字符每个字符依然是一个字节,和传统的ASCII编码兼容。如果第一个字节的高端bit是110,则说明需要2个字节;后续的每个高端bit都以10开头.
- 具体规则如下
0xxxxxxx runes 0-127 (ASCII)
110xxxxx 10xxxxxx 128-2047 (values <128 unused)
1110xxxx 10xxxxxx 10xxxxxx 2048-65535 (values <2048 unused)
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 65536-0x10ffff (other values unused)
我们来找个实际的例子来看看
对于测试test
这个字符串
它的情况如下
字符: 27979 35797 116 101 115 116
字节: 230 181 139 232 175 149 116 101 115 116
230的二进制是11100110
, 说明是3个字节, 然后后面的181,和139都是
10`开头的, 然后232同理
可以看到, 中文字符这里占了三个字节, 而英文字母是一个字节, 比如116就是01110100
, 一个字节
关于utf-8
- 节省空间的原因在于变长
- 每个字符的可用空间变少, eg: 对于4个字节的来说, 有效bit是3+6x3 = 21bit, 对于3个字节, 4+ 6x2 = 16个bit, 利用率随着字节数变多, 而降低,
- 但是, 多字节的字符用的很少其实 , 主要还是1个字节的和2-3个字节的
- 保证了完整性的同时, 牺牲了空间效率, 但又因为高频使用部分局限, 所以又保证了部分空间效率