1.双引号与反单引号的区别
字符串可以使用双引号赋值,也可以使用反单引号赋值(注意不是单引号),它们的区别在于对特殊字符的处理。
假如,我们希望string变量表示下面的字符串,它包括换行符和双引号。
Hi,
this is "RainbowMango".
使用双引号表示时,需要对特殊字符转义,如下所示。
S:="Hi, \nthis is \"RainbowMangol\"."
两种方式的对比,
1.使用双引号表示的时候,需要对特殊的字符进行转义
2.使用反单引号表示的时候,不需要对特殊字符进行转义
package main
func main(){
s := `Hi,
this is "RainbowMango"`
print(s,"\n")
s1 := "Hi,\nthis is \"RainbowMango\"."
print(s1)
}
输出如下:
Hi,
this is "RainbowMango"
Hi,
this is "RainbowMango".
2.字符串拼接
字符串可以使用加号进行拼接:
s = s + "a" + "b"
需要注意的是,字符串拼接的时候会出发内存分配以及内存的拷贝,单行语句拼接多个字符串只分配一次内存。在拼接的时候,会先计算最终字符串的长度后再分配内存。
3.类型转换
项目中,数据经常需要在string和字节切片([ ]byte) 之间转换。string和byte[ ]进行转换:
package main
import "fmt"
func main(){
b := []byte{'H', 'e', '1', '1', 'o'}
fmt.Println(b)
fmt.Println(b[0])
s:= string(b)
fmt. Println(s)
}
输出如下:
[72 101 49 49 111]
72
He11o
[ ]byte 转 string类型:
import "fmt"
func main(){
b := "Hello"
a :=[]byte(b)
fmt. Println(a)
}
需要注意的是,无论是字符串转换成[ ]byte,还是[ ]byte转换成string,都将发生一次内存拷贝,会有一定的开销。
4.UTF编码
string使用8比特字节的集合来存储字符,而且存储的是字符的UTF-8编码,例如每个汉字字符的UTF-8编码将占用多个字节。
在使用for-range遍历字符串的时候,每次迭代将返回字符UTF-8编码的首个字节的下表以及字节值,这意味着下标可能不连续。
例如下面的,输出如下图:
此外,字符串的长度是指字节数,而非字符数,比如汉字“中”和“国”的UTF-8编码各占3个字节,字符串“中国”的长度为6而不是2。
import "fmt"
func main(){
s := "中国"
for index , value := range s{
fmt.Printf("index : %d , value: %c\n",index,value)
}
}
index : 0 , value: 中
index : 3 , value: 国
5.字符串的值不可修改
字符串可以为空,但值不会是nil。另外,字符串不可以修改。字符串变量可以接受新的字符串赋值,但不能通过下标方式修改字符串中的值。
比如下面的代码,尝试将字母“H”修改为小写:
s :="Hello"
&s[0] = byte (104)//非法
s = "hello"//合法
字符串不支持取地址操作,也就无法修改字符串的值,上面的语句中会出现编译错误:
cannot take the address of s[0]
6.string和[]byte如何取舍
string和[]byte都可以表示字符串,但因数据结构不同,其衍生出来的方法也不同,要根据实际应用场景来选择。
string擅长的场景:
需要字符串比较的场景;
不需要nil字符串的场景。
[ ]byte 擅长的场景:
修改字符串,尤其是修改粒度为1个字节的场景;
函数返回值,需要用nil表示含义的场景;
需要切片操作的场景。
虽然看起来string适用的场景不如[]byte多,但因为string直观,在实际应用中还是大量存在的,在偏底层的实现中[ ]byte使用得更多。