首先看下以下例子:
package main
import "fmt"
func main(){
s := "我是中国人"
for i:=0; i < len(s); i++{
fmt.Printf("%c", s[i])
}
fmt.Printf("\n")
for _, v := range s {
fmt.Printf("%c", v)
}
fmt.Print("\n")
}
输出结果:
ææ¯ä¸å½äºº
我是中国人
通过len(s)和range遍历访问字符串元素有什么不同么?
首先来复习一下go语言的字符串表示,go语言有以下两种表示字符串的方法:
1、双引号,如:“gogogo\n”,使用转义字符
2、反引号,如:`gogogo\n`,不使用转义字符,字符串的内容将和赋值保持严格一致
在go语言中,没有字符类型,rune就是字符类型
Go 语言中的字符串是以 UTF-8 格式编码并存储的,例如:
s := "Hello 世界!"
变量 s 中存放的是这个字符串的 UTF-8 编码,当你使用 len(s) 函数获取字符串的长度时,获取的是该字符串的 UTF-8 编码长度。通常我们认为,在电脑中存储一个 ASCII 字符需要一个字节,存储一个非 ASCII 字符需要两个字节,这种认为仅仅是针对 Windows 系统中常用的 ANSI 编码而言的,而在 Go 语言中,使用的是 UTF-8 编码,用 UTF-8 编码来存放一个 ASCII 字符依然只需要一个字节,而存放一个非 ASCII 字符,则需要 2个、3个、4个字节,它是不固定的。
既然 Go 中的字符串存放的是 UTF-8 编码,那么我们使用 s[0] 这样的下标方式获取到的内容就是 UTF-8 编码中的一个字节。对于非 ASCII 字符而言,这样的一个字节没有实际的意义,除非你想编码或解码 UTF-8 字节流。而在 Go 语言中,已经有很多现成的方法来编码或解码 UTF-8 字节流了。
遍历字符串中的字节(使用下标访问):
func main() {
s := "Hello 世界!"
for i, l := 0, len(s); i < l; i++ {
fmt.Printf("%2v = %v\n", i, s[i]) // 输出单个字节值
}
}
遍历字符串中的字符(使用 for range 语句访问):
func main() {
s := "Hello 世界!"
for i, v := range s { // i 是字符的字节位置,v 是字符的拷贝
fmt.Printf("%2v = %c\n", i, v) // 输出单个字符
}
}
在 Go 语言中,字符串的内容是不能修改的,也就是说,你不能用 s[0] 这种方式修改字符串中的 UTF-8 编码,如果你一定要修改,那么你可以将字符串的内容复制到一个可写的缓冲区中,然后再进行修改。这样的缓冲区一般是 []byte 或 []rune。如果要对字符串中的字节进行修改,则转换为 []byte 格式,如果要对字符串中的字符进行修改,则转换为 []rune 格式,转换过程会自动复制数据。
修改字符串中的字节(用 []byte):
func main() {
s := "Hello 世界!"
b := []byte(s) // 转换为 []byte,自动复制数据
b[5] = ',' // 修改 []byte
fmt.Printf("%s\n", s) // s 不能被修改,内容保持不变
fmt.Printf("%s\n", b) // 修改后的数据
}
修改字符串中的字符(用 []rune):
func main() {
s := "Hello 世界!"
r := []rune(s) // 转换为 []rune,自动复制数据
r[6] = '中' // 修改 []rune
r[7] = '国' // 修改 []rune
fmt.Println(s) // s 不能被修改,内容保持不变
fmt.Println(string(r)) // 转换为字符串,又一次复制数据
}
在 []byte 中处理 Rune 字符(需要用到 utf8 包中的解码函数)
func main() {
b := []byte("Hello 世界!")
for len(b) > 0 {
r, n := utf8.DecodeRune(b) // 解码 b 中的第一个字符
fmt.Printf("%c\n", r) // 显示读出的字符
b = b[n:] // 丢弃已读取的字符
}
}
有疑问加站长微信联系(非本文作者)