Strings、bytes and runes -- 就要學習 Go 語言

Go 中的字符串值得特別關注,與其餘語言相比,Go 中的字符串實現方式有所不一樣。html

字符串

在Go中,使用雙引號 "" 聲明字符串:golang

s := "Hello world"
fmt.Println("len(s):",len(s))
fmt.Println(s);       
複製代碼

輸出:函數

len(s): 11
Hello world
複製代碼

上面的代碼聲明瞭字符串 slen 函數返回字符串 s 的字節數(包括空格)。在 Go 中,字符串實際上是隻讀的字節切片post

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 個字節表示一個符號。ui

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 格式化輸出值的類型。從結果能夠看出,值的類型都是 uint8byte 類型,byteuint8 的別名,byte 類型在個人文章中有所介紹。 咱們來看下:一個字符串中包含非 ASCII 碼的字符 會是怎樣的狀況。spa

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])
}
複製代碼

輸出.net

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 編碼 佔兩個字節 c3b5for 循環按字節讀取,c3 (十進制 195 )對應字符 Ãb5 (十進制 181 )對應字符 µ詳見這裏)。 熟悉 ASCIIUTF-8Unicode 更有利於理解這些知識,關於這些知識,不會在本文展開細講,有興趣的能夠參考這裏UTF-8 編碼中,一個碼點佔用至少一字節,若是咱們仍是以一個碼點佔用一個字節去打印字符確定會出問題,就像上面的例子同樣。那有沒有辦法解決這個問題,好在 Go 爲咱們提供了 runecode

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 ,印證了 runeint32 的別名。

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]

這篇文章有幾點比較重要

  1. 字符串是隻讀的字節切片;
  2. rune 表示 Go 中的 Unicode 代碼點;
  3. Go 採用 UTF-8 編碼,這種編碼方式是 Unicode 的實現方式之一;
  4. 熟悉 ASCIIUTF-8Unicode ,能夠參考 ,更有利於理解這篇文章;

但願這篇文章可以解決你對 Go string 的一些疑問,有不懂的能夠留言討論!


(全文完)

原創文章,若需轉載請註明出處!
歡迎掃碼關注公衆號「Golang來啦」或者移步 seekload.net ,查看更多精彩文章。

公衆號「Golang來啦」給你準備了一份神祕學習大禮包,後臺回覆【電子書】領取!

公衆號二維碼
相關文章
相關標籤/搜索