# 7.6 字符串、数组和切片的应用 ## 7.6.1 从字符串生成字节切片 假设 s 是一个字符串(本质上是一个字节数组),那么就可以直接通过 `c := []bytes(s)` 来获取 c 为一个元素类型为字节的切片。另外,您还可以通过 copy 函数来实现:`copy(dst []byte, src string)`. 同样的,还可以使用 for-range 语法来获得每个元素(Listing 7.13—for_string.go): ```go package main import "fmt" func main() { s := "\u00ff\u754c" for i, c := range s { fmt.Printf("%d:%c ", i, c) } } ``` 输出: 0:ÿ 2:界 我们发现,Unicode 字符会占用 2 个字节,有些甚至需要 3 个或者 4 个字节来进行表示。如果发现错误的 UTF8 字符,则该字符会被设置为 U+FFFD 并直接向前移动一个字节。和字符串转换一样,您同样可以使用 `c := []int(s)` 语法,这样切片中的每个 int 都会包含对应的 Unicode 代码,因为字符串中的每次字符都会对应一个整数。类似的,您也可以将字符串转换为元素类型为 rune 的切片:`r := []rune(s)`。 可以通过代码 `len([]int(s))` 来获得字符串中字符的数量,但使用 `utf8.RuneCountInString(s)` 效率会更高一点。 您还可以将一个字符串追加到某一个字符数组的尾部: ```go var b []byte var s string b = append(b, s...) ``` ## 7.6.2 获取字符串的某一部分 使用 `substr := str[start:end]` 可以从字符串 str 获取到从索引 start 开始到 end-1 位置的子字符串。同样的,`str[start:]` 则表示获取从 start 开始到 len(str)-1 位置的子字符串。而 `str[:end]` 表示获取从 0 开始到 end-1 的子字符串。 ## 7.6.3 字符串和切片的内存结构 在内存中,一个字符串实际上是一个双字结构,即一个指向实际数据的指针和记录字符串长度的整数(见图 7.4)。因为指针对用户来说是完全不可见,因此我们可以依旧把字符串看做是一个值类型,也就是一个字符数组。 字符串 `string s = "hello"` 和子字符串 `t = s[2:3]` 在内存中的结构可以用下图表示: ![](images/7.6_fig7.4.png) ## 7.6.4 修改字符串中的某个字符 Go 语言中的字符串是不可变的,也就是说 `str[index]` 这样的表达式是不可以被放在等号左侧的。如果尝试运行 `str[i] = ‘D’` 会得到错误:`cannot assign to str[i]`。 因此,您必须先将字符串转换成字节数组,然后再通过修改数组中的元素值来达到修改字符串的目的,最后将字节数组转换会字符串格式。 例如,将字符串 "hello" 转换为 "cello": ```go s := "hello" c := []byte(s) c[0] = ’c’ s2 := string(c) // s2 == "cello" ``` 所以,您可以通过操作切片来完成对字符串的操作。 ## 7.6.5 字节数组对比函数 180