Go 中的字串值得特別關注,與其他語言相比,Go 中的字串實現方式有所不同。
字串
在Go中,使用雙引號 ""
宣告字串:
s := "Hello world"
fmt.Println("len(s):",len(s))
fmt.Println(s);
複製程式碼
輸出:
len(s): 11
Hello world
複製程式碼
上面的程式碼宣告瞭字串 s
,len
函式返回字串 s
的位元組數(包括空格)。在 Go 中,字串其實是隻讀的位元組切片。
s := "Hello world"
for i:=0;i<len(s);i++ {
fmt.Print(s[i]," ")
}
複製程式碼
你覺得上面的程式碼會輸出什麼,是一個個字母嗎?其實不是:
72 101 108 108 111 32 119 111 114 108 100
複製程式碼
輸出的是每個字母在 ASCII 碼錶上對應的十進位制數字。
正如大家熟知的,Go 語言採用 UTF-8
編碼,這種編碼方式與 ASCII
編碼相容,只不過 ASCII
編碼只需 1 個位元組,而 UTF-8
需要 1-4 個位元組表示一個符號。
s := "Hello world"
for i:=0;i<len(s) ;i++ {
fmt.Printf("%c ",s[i])
}
fmt.Println("")
for i:=0;i<len(s) ;i++ {
fmt.Printf("%v ",s[i])
}
fmt.Println("")
for i:=0;i<len(s) ;i++ {
fmt.Printf("%x ",s[i])
}
fmt.Println("")
for i:=0;i<len(s) ;i++ {
fmt.Printf("%T ",s[i])
}
複製程式碼
輸出:
H e l l o w o r l d
72 101 108 108 111 32 119 111 114 108 100
48 65 6c 6c 6f 20 77 6f 72 6c 64
uint8 uint8 uint8 uint8 uint8 uint8 uint8 uint8 uint8 uint8 uint8
複製程式碼
上面的程式碼,%v
格式化輸出位元組對應的十進位制值;%x
以十六進位制輸出;%T
格式化輸出值的型別。從結果可以看出,值的型別都是 uint8
即 byte
型別,byte
是 uint8
的別名,byte
型別在我的文章中有所介紹。
我們來看下:一個字串中包含非 ASCII
碼的字元 會是怎樣的情況。
s := "Hellõ World"
fmt.Println("len(s):", len(s))
for i := 0; i < len(s); i++ {
fmt.Printf("%c ", s[i])
}
fmt.Println("")
for i := 0; i < len(s); i++ {
fmt.Printf("%v ", s[i])
}
fmt.Println("")
for i := 0; i < len(s); i++ {
fmt.Printf("%x ", s[i])
}
複製程式碼
輸出:
len(s): 12
H e l l à µ W o r l d
72 101 108 108 195 181 32 87 111 114 108 100
48 65 6c 6c c3 b5 20 57 6f 72 6c 64
複製程式碼
上面的例子中,將 o
替換成 õ
。從結果可以看出,字串的位元組長度是 12 ,說明 õ
佔用兩個位元組。然而 õ
的輸出變成了 Ã µ
, õ
的 Unicode
碼點是 U+00F5
,其 UTF-8 編碼 佔兩個位元組 c3
、b5
。for
迴圈按位元組讀取,c3
(十進位制 195 )對應字元 Ã
, b5
(十進位制 181 )對應字元 µ
(詳見這裡)。
熟悉 ASCII
、UTF-8
和 Unicode
更有利於理解這些知識,關於這些知識,不會在本文展開細講,有興趣的可以參考這裡。
UTF-8
編碼中,一個碼點佔用至少一位元組,如果我們還是以一個碼點佔用一個位元組去列印字元肯定會出問題,就像上面的例子一樣。那有沒有辦法解決這個問題,好在 Go 為我們提供了 rune
。
Rune
rune
是 Go 的內建資料型別,是 int32
的別名,表示 Go 中的 Unicode
程式碼點。用 rune
資料型別,開發人員就不必關心程式碼點佔用幾個位元組了。
s := "Hellõ World"
r := []rune(s)
fmt.Println("len(r):", len(r))
for i := 0; i < len(r); i++ {
fmt.Printf("%c ", r[i])
}
fmt.Println("")
for i := 0; i < len(r); i++ {
fmt.Printf("%v ", r[i])
}
fmt.Println("")
for i := 0; i < len(r); i++ {
fmt.Printf("%x ", r[i])
}
fmt.Println("")
for i := 0; i < len(r); i++ {
fmt.Printf("%T ", r[i])
}
複製程式碼
輸出:
len(r): 11
H e l l õ W o r l d
72 101 108 108 245 32 87 111 114 108 100
48 65 6c 6c f5 20 57 6f 72 6c 64
int32 int32 int32 int32 int32 int32 int32 int32 int32 int32 int32
複製程式碼
上面的程式碼,字串 s
通過型別轉化成了 rune
切片。 õ
的 Unicode
碼點就是 U+00F5
,對應十進位制的 245 ,參考這。切片 r
的長度就是:11 ;輸出的 int32
,印證了 rune
是 int32
的別名。
for range
字串
上面的例子已經很好解決了之前遇到的問題,有種更好的方式 -- range string
。使用 range
迴圈一個字串,將返回 rune
型別的字元和位元組索引。
s := "HellõWorld"
for index, char := range s {
fmt.Printf("%c starts at byte index %d \n", char,index)
}
複製程式碼
輸出:
H starts at byte index 0
e starts at byte index 1
l starts at byte index 2
l starts at byte index 3
õ starts at byte index 4
W starts at byte index 6
o starts at byte index 7
r starts at byte index 8
l starts at byte index 9
d starts at byte index 10
複製程式碼
從輸出結果可以看出,õ
佔用了兩個位元組:索引 4 和 5。
文章讀到這,可能會有個疑問,怎麼獲取字串的長度呢?
Length of the string
可以使用 RuneCountInString()
函式,原型是這樣的:
func RuneCountInString(s string) (n int)
複製程式碼
返回字串中 rune
字元的個數。
s := "Hellõ 中國"
length := utf8.RuneCountInString(s)
fmt.Println(length)
複製程式碼
輸出:8
字串是不可變的
前面我們已經說過,字串是隻讀的位元組切片,一旦建立,是不可更改的。如果強制修改,就會報錯:
s := "Hello World"
s[0] = "h"
複製程式碼
報錯:cannot assign to s[0]
這篇文章有幾點比較重要:
- 字串是隻讀的位元組切片;
rune
表示 Go 中的Unicode
程式碼點;- Go 採用
UTF-8
編碼,這種編碼方式是Unicode
的實現方式之一; - 熟悉
ASCII
、UTF-8
和Unicode
,可以參考 這,更有利於理解這篇文章;
希望這篇文章能夠解決你對 Go string
的一些疑問,有不懂的可以留言討論!
(全文完)
原創文章,若需轉載請註明出處!
歡迎掃碼關注公眾號「Golang來啦」或者移步 seekload.net ,檢視更多精彩文章。
公眾號「Golang來啦」給你準備了一份神祕學習大禮包,後臺回覆【電子書】領取!