基础
type _string struct {
elements *byte // 引用着底层的字节
len int // 字符串中的字节数
}
- 可以用运算符+和+=来衔接字符串。
- 字符串类型都是可比较类型。
- 字符串值的内容(即底层字节)是不可更改的。 字符串值的长度也是不可独立被更改的。 修改应该重新赋新值;或者转换成字节切片(重新开辟一段内存,复制原先内容)再修改。
- 使用容器元素索引语法
str[i]
来获取str
中的第i
个字节。 表达式str[i]
是不可寻址的。(也不可修改) 一个子切片表达式
str[start:end]
的估值结果也将和基础字符串str
共享一部分底层字节字符串和字节切片之间的转换
一个字符串值可以被显式转换为一个字节切片(byte slice),反之亦然。 一个字节切片类型是一个元素类型的底层类型为内置类型byte
的切片类型。当一个字符串被转换为一个字节切片时,结果切片中的底层字节序列是此字符串中存储的字节序列的一份深复制。 即Go运行时将为结果切片开辟一块足够大的内存来容纳被复制过来的所有字节。(反之也是深复制)。一个字节切片和一个字符串是不能共享底层字节序列的。
优化
避免深复制的情况:
- 一个
for-range
循环中跟随range
关键字的从字符串到字节切片的转换; - 一个在映射元素读取索引语法中被用做键值的从字节切片到字符串的转换(注意:对修改写入索引语法无效);
- 一个字符串比较表达式中被用做比较值的从字节切片到字符串的转换;
- 一个(注意:至少有一个被衔接的字符串值为非空字符串常量的)字符串衔接表达式中的从字节切片到字符串的转换。
// 情况一
var str = "world"
// 这里,转换[]byte(str)将不需要一个深复制。
for i, b := range []byte(str) {
fmt.Println(i, ":", b)
}
key := []byte{'k', 'e', 'y'}
m := map[string]string{}
// 情况二的注意事项
// 这个string(key)转换仍然需要深复制。
m[string(key)] = "value"
// 情况二
// 这里的转换string(key)将不需要一个深复制。
// 即使key是一个包级变量,此优化仍然有效。
fmt.Println(m[string(key)])
var s string
var x = []byte{1023: 'x'}
var y = []byte{1023: 'y'}
// 下面的四个转换都不需要深复制。
// 情况三
if string(x) != string(y) {
// 情况四
s = (" " + string(x) + string(y))[1:]
}
// 两个在比较表达式中的转换不需要深复制,
// 但两个字符串衔接中的转换仍需要深复制。
// 情况四的注意事项。
if string(x) != string(y) {
s = string(x) + string(y)
}
字符串和码点切片之间的转换
一个字符串值可以被显式转换为一个码点切片(rune slice),反之亦然。 一个码点切片类型是一个元素类型的底层类型为内置类型rune
的切片类型。在一个从码点切片到字符串的转换中,码点切片中的每个码点值将被UTF-8编码为一到四个字节至结果字符串中。当一个字符串被转换为一个码点切片时,此字符串中存储的字节序列将被解读为一个一个码点的UTF-8编码序列。
字符串衔接
+
运算符fmt
标准库包中的Sprintf/Sprint/Sprintln
函数bytes
标准库包提供的Buffer
类型可以用来构建一个字节切片,然后我们可以将此字节切片转换为一个字符串strings
标准库包中的Builder
类型可以用来拼接字符串,,此类型内部也维护着一个字节切片,但是它在将此字节切片转换为字符串时避免了底层字节的深复制
将字符串当作字节切片使用
hello := []byte("Hello ")
world := "world!"
// 语法糖
helloWorld := append(hello, world...)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。