Golang string
字符串的本质
Golang中的string,实际是一个不可变的字节序列,每个字节都是UTF-8编码的文本。 虽然每一个字符串都有长度,但是这个长度和数组不一样,不同长度的字符串还是同样的string类型。
我们来看一下字符串的结构,在reflect.StringHeader中,我们可以看到定义:
type StringHeader struct {
Data uintprt
Len int
}
从这里我们可以看到,string其实是一个结构,Data持有的是底层字节数组,Len就是字符串的长度了。 因此对string的复制,也就是StringHeader结构体的复制,并不涉及到底层字节数组的复制,所以没有什么消耗。 另外,相同的字符串面值的常量,对应的一般都是同一个字节数组。 string虽然底层不是切片,但是支持切片的操作。
字符串的使用
遍历字符串
遍历字符串,是使用range
来进行的,虽然for也可以遍历字符串的内容,但是会有问题:
func main() {
s := "123"
for i := 0; i < len(s); i++ {
fmt.Println(s[i])
}
}
output:
49
50
51
等会儿,这个有点和想象的不一样啊,为什么不是123的输出呢? 这是因为Golang是UTF-8编码,123对于的码的值就是这些数字。 我们可以转换成字符再打印。但是还会有其他问题
func main() {
s := "hi,世界"
for i := 0; i < len(s); i++ {
fmt.Printf("%c\n", s[i], s[i])
}
}
output:
h
i
,
ä
¸
ç
这里又出现乱码了,这个是因为UTF-8在编码的时候,是变长编码的,对于ASCII是采用一个byte来编码的, 而对于其他Unicode字符来说则是两个字节的编码。我们通过for来遍历,其实还是在遍历底层的字节序列。
对于这个,我们当然可以转成Golang中的[]rune
类型。(话说用rune,符文,这个词,对于西方世界还是挺形象的)。
[]rune
其实是[]int32
的别名,而不是重新定义的新类型。
func main() {
s := "hi,世界"
r := []rune(s)
for i := 0; i < len(r); i++ {
fmt.Printf("%c\n", r[i])
}
}
output:
h
i
,
世
界
这次就对了。
但是一般情况下,我们不需要这么麻烦,是可以用range来遍历的:
func main() {
s := "hi,世界"
for _, v := range s {
fmt.Printf("%c\n", v)
}
}
output:
h
i
,
世
界
这是因为Golang中的range针对string做了特殊的处理。
截取字符串
截取其中一部分字符串是非常容易的,因为字符串是支持切片操作的。只要substr := str[m:n]
即可。
改变中某个字符
因为字符串是不支持直接修改的,所以必须曲线救国。 我们已经知道,字符串是UTF-8编码的了,那么对于只有ASCII的包含非ASCII字符的两种string,可以有不同的改法。
如果字符串只包含ASCII字符,那么可以将string
转换成[]byte
,然后再来修改。
func main() {
s := "123"
bs := []byte(s)
bs[1] = 'a'
s = string(bs)
fmt.Println(s)
// “1a3”
}
如果字符串中包含Unicode字符,那么需要将string
转换成[]rune
,然后再进行修改。
func main() {
s := "hi,世界"
rs := []rune(s)
rs[3] = 'w'
s = string(rs)
fmt.Println(s)
// “hi,w界”
}
计算长度
虽然string支持len
操作,但是这个取出来的是底层byte数组的长度。
所以如果字符串全部都是ASCII,可以使用len来获取长度,如果包含unicode,则需要使用utf8.RuneCountInString(str)
。
func main() {
s := "123"
println(len(s)) // 3
s = "hi,世界"
println(len(s)) // 9
println(utf8.RuneCountInString(s)) // 5
}
字符串的拼接
字符串的拼接可以有以下几种方式:
- 最简单的,使用
+
- 最高效的,使用字节拼接
使用strings包的方法
func main() { s1 := "hi" s2 := "世界" // 1 println(s1 + s2) // 2 var buf bytes.Buffer buf.WriteString(s1) buf.WriteString(s2) println(buf.String()) // 3 str := strings.Join([]string{s1, s2}, "") println(str) }
字符串与数字的转换
在实际的使用过程中,我们经常会需要将字符串与数字进行转换,可以通过strconv
提供的各种函数来实现。
源类型 | 目标类型 | 方式 |
---|---|---|
string | int | string, _ := strconv.Atoi(“1”) |
int | string | int := strconv.Itoa(1) |
string | int64 | int64, _ := strconv.ParseInt(“1”, 10, 64) |
int64 | string | string := strconv.FormatInt(1, 64) |