讲清楚 string 和 bytes[], 字符,字面量,unicode编码, rune。
专栏的介绍可以参考 《GotchaGolang专栏》,代码可以看《宝库-Gotcha》。
为什么 string 不可变,[]bytes 可变?
字符串是一段只读切片,存储的是字节;utf8 编码的字符,字节数不确定,所以 string 再按照 C 语言的习惯(ascii编码的字符)去操作字符串,势必出问题,好在这方面 golang 语言自己已经做了一定的处理, fmt包,range循环, encoding/utf8包等。
下面慢慢看看详情。
golang 中的字符串,不管是字面量,还是先定义 string 后赋值的都是只读的,要操作必须先转换层字符串。
此外,byte slice 也可以转化成字符串。这两种转化都需要分配一块新的内存,然后进行内容拷贝。
1 | s := "abc" |
运行结果如下:
1 | package main |
string 截取操作没有改变底层数组(即重新分配内存)但是关于底层数组的索引已经变了,长度改变。
1 | package main |
字符串实际上是一个只读的片段,保存任意字节。
为 字符串保存的是字节
, 另一部分就是 字符的含义很难定义
,UTF8的字符可能是2-6字节(utf16,像 Qt 里面的 QString 则明确的说用 utf16 进行内部字符串编码;utf8, ascii 都有它不方便的地方),rune 就专门用来定义单个字符Unicode编码,而不仅仅是 int32 的别名。
rune : fmt.Println(str[2]); //108 ,打印的数字就表明了 rune 代表的是该字节对应 Unicode 编码
而对于上面的 "hello"
根本没有字节级别的转义,所以不能取按照字节赋值。打印是经过特殊处理了,也就是说不仅仅可以按小标打印字面量,也可以通过循环打印,他们已经解码 utf8的rune:
1 | const nihongo = "日本語" |
循环输出按照 unicode 编码来的,可以看到字节占用: (索引操作是按照字节来的)
1 | U+65E5 '日' starts at byte position 0 |
上面的串,不是英文字母,也就是一个字符占用多过一个字节,那么再用字节操作,即下标索引,那么打印就很奇怪了。
当然,如果在 string 字面量里面初始化一堆无效的 unicode,打印出来的状况很诡异的。
一定要自己去处理和组合这些 utf8 编码的字节,或者称 string,unicode/utf8
标准库操作:
(用的很少,特殊需求)
1 | const nihongo = "日本語" |
字节、字符、rune之间的区别,UTF-8编码和Unicode编码之间的区别, 一个字符串和一个字符串字面量的区别。
扩展阅读:《string, bytes, runes, chars》。
与其这样采用 utf8,然后为了适配字符和字节问题专门弄一个 utf8 包,还不如一开始采用 utf16呢。笑。(那样占用的控件就大了)其他关于 string 和 []byte 的操作可以借助标准库 strings
和 bytes
。
Merlin 2018.3 string 和 []bytes 之间的恩怨