Go 語言中定義字符串要用雙引號,而不是單引號,單引號中只能包含一個元素,表示一個字符。
Go語言中字符有兩種類型,分別是:
uint8
類型,或者叫byte
型,代表了 ASCII 碼的一個字符rune
類型,代表一個 UTF-8 字符
了解編碼知識的應該知道,ASCII 編碼字符是1個字節的,而 UTF-8 是可變長的編碼,當要表示中文等非 ASCll 編碼的字符時,需要使用 UTF-8 編碼來保證不會亂碼。關於字符編碼相關知識,推薦看這篇廖雪峰的介紹 字符編碼
假如我們要遍歷輸出一個包含中文的字符串時:
package main
import "fmt"
func main() {
str := "hello 世界"
for i := 0; i < len(str); i++ {
fmt.Println(str[i])
}
}
上述代碼的打印結果是:
104
101
108
108
111
32
228
184
150
231
149
140
Go 語言字符串存儲的其實是類型為 byte 的只讀切片,或者說一個字符串就是一堆字節。在 UTF-8 編碼中一個英文字符可以用一個字節存儲,一個中文字符需要三個或四個字節存儲,而 ASCII 碼符號只有 128 個,大於 128 的都不在范圍內。上述代碼的遍歷方式就是以 ASCII 類型來讀字符的。
可以看到輸出結果中的前 6 個都是在 ASCII 表中的,但是后 6 個就不在表中了,后面 6 個的每一個字節都不能單獨表示一個字符,而是用 3 個在一起才能表示一個字符,明顯超出了 ASCII 表的范圍,所以當打印具體字符時就會亂碼,比如我們現在來打印每一個真實字符,需要使用到占位符 %c
:
package main
import "fmt"
func main() {
str := "hello 世界"
for i := 0; i < len(str); i++ {
fmt.Printf("%c", str[i]) // hello ä¸ç
}
}
輸出結果為:
hello ä¸ç
明顯亂碼了。解釋這么多就是為了說明如果字符串中有非 ASCII 碼的字符時,就不能使用 byte
來表示字符,需要使用 rune
類型來表示。
使用 rune 類型來遍歷字符串
在 Go 中,有一個遍歷方式是 range
,它默認就是以 UTF-8 編碼形式去讀每一個字符。當涉及到的字符串中含有非英文字符時,可以使用 range
來遍歷:
package main
import "fmt"
func main() {
str := "hello 世界"
for _, r := range str {
fmt.Println(r)
}
}
輸出結果為:
104
101
108
108
111
32
19990
30028
此時輸出的字節編碼就是 UTF-8 編碼號,UTF-8 編碼是包含 ASCII 編碼的,所以前 6 個編號還是一樣的,后面兩個編號分別代表世
,界
。
修改字符串
Go 語言中對字符串的修改其實不是對字符串本身的修改,而是復制字符串,同時修改值,即重新分配來內存,需要先將字符串轉化成數組,[]byte
或 []rune
,然后再轉換成 string 型。
那么我要說的也很明顯了,就是要區別使用 []byte
或 []rune
。
對於全是ASCII編碼的字符串,使用 []byte
即可:
package main
import "fmt"
func main() {
str := "abc"
s2 := []byte(str)
s2[0] = 'b'
fmt.Println(string(s2)) //bbc
}
// string()表示強制類型轉換,轉換為字符串
對於包含中文等字符的字符串時,那就要用 []rune
了:
func main() {
str:="白貓"
s2:=[]rune(str)
s2[0]='黑'
fmt.Println(string(s2)) //黑貓
}
總結
在處理字符時,要考慮字符的編碼范圍,然后根據需要使用 byte
類型或 rune
類型。
byte
類型只能正常輸出 ASCII 編碼范圍的字符;rune
類型可以輸出 UTF-8 編碼范圍的字符。