A string is a slice of bytes in Go.
Strings in Go are Unicode compliant and are UTF-8 Encoded.
即在golang中,字符串使用uft-8进行编码,底层是字节数组([]byte
).
字符串声明
func main() {
str1 := "单行字符串"
fmt.Println(str1)
str2 := `多行字符串
hello world
`
fmt.Println(str2)
}
访问字符串底层字节
func printBytes(s string) {
fmt.Printf("Bytes: ")
for i := 0; i < len(s); i++ {
// %v 输出字节本身的值,%x 以16进制输出
fmt.Printf("%x(%v) ", s[i], s[i])
}
// 输出:
//Bytes: e4(228) bd(189) a0(160) e5(229) a5(165) bd(189)
}
func main() {
name := "你好"
fmt.Printf("String: %s\n", name)
printBytes(name)
}
从上面的输出可知,golang中,一个中文字符占3个字节。
访问字符串的单个字符
例如,依次输出hello
字符串的每个字符。
func printChars(s string) {
fmt.Printf("Characters: ")
for i := 0; i < len(s); i++ {
// %c the character represented by the corresponding Unicode code point
// 即输出Unicode形式的字符
fmt.Printf("%c ", s[i])
}
//输出:Characters: h e l l o
}
func main() {
name := "hello"
fmt.Printf("String: %s\n", name)
printChars(name)
}
上述代码看似没有问题,因为 len(s)
返回字符串的字节数,而对于英文字符串一个字符正好占用一个字节,因此使用 for
循环遍历没有问题。
但是,如果是中文字符串,一个字符占用三个字节,使用上述方式就有问题了。
func printChars(s string) {
fmt.Printf("Characters: ")
for i := 0; i < len(s); i++ {
fmt.Printf("%c ", s[i])
}
// 输出:Characters: ä ½ å ¥ ½
}
func main() {
name := "你好"
fmt.Printf("String: %s\n", name)
printChars(name)
}
使用Rune访问字符串单个字符
rune 是golang中的内置类型,也是int32的别名类型,专用于存储Unicode编码
的单个字符,而不管这个字符占用多少个字节。
下面的示例,我们将字符串转换为 rune切片再进行输入,对别如下:
func printCharsWithRune(s string) {
fmt.Printf("Characters: ")
runestr := []rune(s)
for i := 0; i < len(runestr); i++ {
fmt.Printf("%c ", runestr[i])
}
fmt.Println()
}
func main() {
name1 := "你好"
name2 := "hello"
printCharsWithRune(name1)
printCharsWithRune(name2)
// 分别输出:
// Characters: 你 好
// Characters: h e l l o
}
使用for range loop 访问字符串字符
func printCharsWithRuneAndForRange(s string) {
for index, runes := range s {
fmt.Printf("%c starts at byte %d\n", runes, index)
}
}
func main() {
name := "你好"
printCharsWithRuneAndForRange(name)
}
//输出
//你 starts at byte 0
//好 starts at byte 3
通过切片创建字符串
func main() {
str := "你好"
bytes := []byte(str)
fmt.Println(bytes)
// byteSlice := []byte(bytes)
byteSlice := []byte{228, 189, 160, 229, 165, 189}
str1 := string(byteSlice)
fmt.Println(str1)
}
// 输出
//[228 189 160 229 165 189]
//你好
当然,除了十进制,也可以使用16进制的字节切片
func main() {
byteSlice := []byte{0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd}
str1 := string(byteSlice)
fmt.Println(str1)
}
// 输出: 你好
当然,如果接受的是Unicode,也可以通过 []rune
切片创建字符串。
func main() {
str := "你好"
textQuoted := strconv.QuoteToASCII(str)
// 获取字符串的 Unicode code
textUnquoted := textQuoted[1 : len(textQuoted)-1]
fmt.Println(textUnquoted) // 输出\u4f60\u597d
runeSlice := []rune{0x4F60, 0x597D}
str1 := string(runeSlice)
fmt.Println(str1) // 输出:你好
}
获取字符串的长度
我们知道 len(s)
可以获取字符串的字节数,那如果要获取字符串的字符数呢? 可以使用 RuneCountInString(s string) (n int)
func main() {
str := "你好"
fmt.Printf("字符数:%d\n", utf8.RuneCountInString(str))
fmt.Printf("字节数:%d", len(str))
}
//输出:
//字符数:2
//字节数:6
字符串的比较和拼接
在golang 中,比较字符串相等,可以使用 ==
操作符合。
而,拼接字符串有两种方式:
- 使用
+
操作符。 - 使用
Sprintf
格式化输出
s1 := "你"
s2 := "好"
fmt.Println(s1 + s2)
fmt.Println(fmt.Sprintf("%s%s", s1, s2))
字符串不可变性
在golang中,字符串时不可变的。
func main() {
// =================== 字符串可不改变性 ===============
// s1 := "你好"
// s1[0] = '他' // 不可直接修改单个rune字符
s1 := "你好"
runes := []rune(s1)
runes[0] = '他' // 注意是单引号
fmt.Println(string(runes)) // 他好
fmt.Println(s1) // 你好
}