字符串、字符、字节
Strings, bytes, runes and characters in Go - The Go Programming Language
结论:
-
go 源码文件总是以 utf-8 编码存储,对于出现在字符串中不能编码的字符,则会以添加逃逸字符的形式存储起来
-
字符串就是只读的
[]byte
。可以包含任意字节,不局限于可 utf-8 编码的字节。所以使用常规的下标获取法获取到的只是一个字节 -
unicode 中每一个字符都有其对应的
code point
,但是有些字符也可以由多个code point
序列来表示 -
这些序列代表了 unicode 概念中的 code points,go 将其称作 rune,也即是字符
-
单引号即表示 rune,另外一种唯一的情况就是使用
for range
循环获取 rune -
由字符串文字构造的字符串(比如下面代码中的 sample),如果不包含字节等级的逃逸符号(比如 \xbd),总是包含合法的 utf-8 序列
-
Go中不保证字符串中的字符是规范化的
总结:
- Go source code is always UTF-8.
- A string holds arbitrary bytes.
- A string literal, absent byte-level escapes, always holds valid UTF-8 sequences.
- Those sequences represent Unicode code points, called runes.
- No guarantee is made in Go that characters in strings are normalized.
验证代码:
func TestGoString(t *testing.T) {
t.Run("string is []byte", func(t *testing.T) {
const sample = "\xbd\xb2\x3d\xbc\x20\xe2\x8c\x98中 Z"
//sample := []byte("\xbd\xb2\x3d\xbc\x20\xe2\x8c\x98中 Z") // 若把 sample 换成 []byte形式, 其与字符串形式的输出一模一样
fmt.Println("Printf with % x:")
fmt.Printf("% x\n", sample)
// %q将转义字符串中任何不可打印的字节序列
fmt.Println("Printf with % q:")
fmt.Printf("%q\n", sample)
/* go 源码中的存储形式。
go 的源码就是以 utf8 格式存储的,所以字面字符串自然也是 utf8。
除非它包含像 sample 中那样的UTF-8中断转义符,否则常规字符串文本也将始终包含有效的UTF-8。
*/
fmt.Println("Printf with % +q:")
fmt.Printf("%+q\n", sample)
fmt.Println()
fmt.Println("Byte loop:")
for i := 0; i < len(sample); i++ {
b := sample[i]
fmt.Printf("%x ", b)
}
fmt.Println()
})
t.Run("raw string, 只包含字面意义文本", func(t *testing.T) {
const placeOfInterest = `⌘ \xbd 走`
fmt.Printf("plain string: ")
fmt.Printf("%s", placeOfInterest)
fmt.Printf("\n")
fmt.Printf("quoted string: ")
// go 源码中的存储形式
fmt.Printf("%+q", placeOfInterest)
fmt.Printf("\n")
// 这些字节就是 unicode 字符十六进制 2318 的 UTF-8 编码。
// those bytes are the UTF-8 encoding of the hexadecimal value 2318.
fmt.Printf("hex bytes: ")
for i := 0; i < len(placeOfInterest); i++ {
fmt.Printf("%x ", placeOfInterest[i])
}
fmt.Printf("\n")
})
t.Run("rune is character in go with a integer value", func(t *testing.T) {
char := 'a' // rune
fmt.Printf("%c\n", char)
const sample = "\xbd中国\x20Z"
fmt.Printf("% x\n", sample)
fmt.Println("Range loop:")
for i, runeVal := range sample {
fmt.Printf("%#U, index at:%d\n", runeVal, i)
}
// Output:
// a
// bd e4 b8 ad e5 9b bd 20 5a
// Range loop:
// U+FFFD '�', index at:0
// U+4E2D '中', index at:1
// U+56FD '国', index at:4
// U+0020 ' ', index at:7
// U+005A 'Z', index at:8
})
}