Go字符串

修改字符串

要修改字符串,需要先将其转换成[]rune或[]byte,完成后再转换为string。无论哪种转换,都会重新分配内存,并复制字节数组。

1
2
3
4
5
6
7
8
9
10
11
func changeString() {
s1 := "hello"
// 强制类型转换
byteS1 := []byte(s1)
byteS1[0] = 'H'
fmt.Println(string(byteS1))
s2 := "博客"
runeS2 := []rune(s2)
runeS2[0] = '狗'
fmt.Println(string(runeS2))
}
  1. 先将这段内存拷贝到堆或者栈上;
  2. 将变量的类型转换成 []byte 后并修改字节数据;
  3. 将修改后的字节数组转换回 string
1
2
3
4
// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string

string是8位字节的集合,通常但不一定代表UTF-8编码的文本。string可以为空,但不能为nil。string的值是不能改变的

Go源代码为 UTF-8 编码格式的,源代码中的字符串直接量是 UTF-8 文本。所以Go语言中字符串是UTF-8编码格式的。

1
2
3
// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune = int32

rune是int32的别名,在所有方面都等同于int32,按照约定,它用于区分字符值和整数值。

rune一个值代表的就是一个Unicode字符,因为一个Go语言中字符串编码为UTF-8,使用1-4字节就可以表示一个字符,所以使用int32类型范围可以完美适配。

见坑

字符串拼接

优先使用 strings.Builder 而不是 +=

子字符串操作及内存泄露

字符串的切分也会跟切片的切分一样,可能会造成内存泄露。例子:有一个handleLog的函数,接收一个string类型的参数log,假设log的前4个字节存储的是log的message类型值,需要从log中提取出message类型,并存储到内存中。下面是相关代码:

1
2
3
4
5
6
7
8
func (s store) handleLog(log string) error {
if len(log) < 4 {
return errors.New("log is not correctly formatted")
}
message := log[:4]
s.store(message)
// Do something
}

使用log[:4]的方式提取出了message,假设参数log是一个包含成千上万个字符的字符串。当使用log[:4]操作时,实际上是返回了一个字节切片,该切片的长度是4,而容量则是log字符串的整体长度。那么实际上存储的message不是包含4个字节的空间,而是整个log字符串长度的空间。所以就有可能会造成内存泄露。如下图所示:

image.png

那怎么避免呢?使用拷贝。将uuid提取后拷贝到一个字节切片中,这时该字节切片的长度和容量都是4。如下:

1
2
3
4
5
6
7
8
9
func (s store) handleLog(log string) error {
if len(log) < 4 {
return errors.New("log is not correctly formatted")
}

message := string([]byte(log[:4]))
s.store(message)
// Do something
}

字符串的长度

内建的 len()函数返回byte的数量,而不是像Python中计算好的unicode字符串中字符的数量。
要在Go中得到相同的结果,可以使用“unicode/utf8”包中的 RuneCountInString()函数。

1
2
3
4
5
6
7
package main

import("fmt""unicode/utf8")

func main(){
data :="♥"
fmt.Println(utf8.RuneCountInString(data))//prints: 1}

理论上说 RuneCountInString()函数并不返回字符的数量,因为单个字符可能占用多个rune。

分享到 评论