字符串

寒江蓑笠翁大约 7 分钟

字符串

在Go中,字符串本质上是一个不可变的只读的字节数组,也是一片连续的内存空间。


字面量

前面提到过字符串有两种字面量表达方式,分为普通字符串和原生字符串。


普通字符串

普通字符串由""双引号表示,支持转义,不支持多行书写,下列是一些普通字符串

"这是一个普通字符串\n"
"abcdefghijlmn\nopqrst\t\\uvwxyz"
这是一个普通字符串
abcdefghijlmn
opqrst  \uvwxyz

原生字符串

原生字符串由反引号表示,不支持转义,支持多行书写,原生字符串里面所有的字符都会原封不动的输出,包括换行和缩进。

`这是一个原生字符串,换行
	tab缩进,\t制表符但是无效,换行
	"这是一个普通字符串"
	
	结束
`
这是一个原生字符串,换行
        tab缩进,\t制表符但是无效,换行
        "这是一个普通字符串"

        结束

访问

因为字符串本质是字节数组,所以字符串的访问形式跟数组切片完全一致,例如访问字符串第一个元素

func main() {
   str := "this is a string"
   fmt.Println(str[0])
}

输出是字节而不是字符

116

切割字符串

func main() {
   str := "this is a string"
   fmt.Println(string(str[0:4]))
}
this

尝试修改字符串元素

func main() {
   str := "this is a string"
   str[0] = 'a' // 无法通过编译
   fmt.Println(str)
}
main.go:7:2: cannot assign to str[0] (value of type byte)

虽然没法修改字符串,但是可以覆盖

func main() {
   str := "this is a string"
   str = "that is a string"
   fmt.Println(str)
}
that is a string

转换

字符串可以转换为字节切片,而字节切片或字节数组也可以转换为字符串,例子如下:

func main() {
   str := "this is a string"
   // 显式类型转换为字节切片
   bytes := []byte(str)
   fmt.Println(bytes)
   // 显式类型转换为字符串
   fmt.Println(string(bytes))
}

字符串的内容是只读的不可变的,无法修改,但是字节切片是可以修改的。

func main() {
	str := "this is a string"
	fmt.Println(&str)
	bytes := []byte(str)
    // 修改字节切片
	bytes = append(bytes, 96, 97, 98, 99)
    // 赋值给原字符串
	str = string(bytes)
	fmt.Println(str)
}

提示

两种类型之间的转换都需要进行数据拷贝,其性能损耗会随着长度的增加而增长。


长度

字符串的长度,其实并不是字面量的长度,而是字节数组的长度,只是大多数时候都是ANSCII字符,刚好能用一个字节表示,所以恰好与字面量长度相等,求字符串长度使用内置函数len,例子如下:

func main() {
   str := "this is a string" // 看起来长度是16
   str2 := "这是一个字符串" // 看起来长度是7
   fmt.Println(len(str), len(str2))
}
16 21

看起来中文字符串比英文字符串短,但是实际求得的长度却比英文字符串长。这是因为在unicode编码中,一个汉字在大多数情况下占3个字节,一个英文字符只占一个字节,通过输出字符串第一个元素可以看出结果:

func main() {
   str := "this is a string"
   str2 := "这是一个字符串"
   fmt.Println(string(str[0]))
   fmt.Println(string(str2[0]))
   fmt.Println(string(str2[0:3]))
}
t // 字母t
è // 意大利语
这 // 中文汉字

拷贝

类似数组切片的拷贝方式,字符串拷贝其实是字节切片拷贝,使用内置函数copy

func main() {
   var dst, src string
   src = "this is a string"
   desBytes := make([]byte, len(src))
   copy(desBytes, src)
   dst = string(desBytes)
   fmt.Println(src, dst)
}

也可以使用strings.clone函数,但其实内部实现都差不多

func main() {
   var dst, src string
   src = "this is a string"
   dst = strings.Clone(src)
   fmt.Println(src, dst)
}

拼接

字符串的拼接使用+操作符

func main() {
   str := "this is a string"
   str = str + " that is a int"
   fmt.Println(str)
}

也可以转换为字节切片再进行添加元素

func main() {
   str := "this is a string"
   bytes := []byte(str)
   bytes = append(bytes, "that is a int"...)
   str = string(bytes)
   fmt.Println(str)
}

以上两种拼接方式性能都很差,一般情况下可以使用,但如果对应性能有更高要求,可以使用strings.Builder

func main() {
   builder := strings.Builder{}
   builder.WriteString("this is a string ")
   builder.WriteString("that is a int")
   fmt.Println(builder.String())
}
this is a string that is a int