一個字符串是一個不可改變的字節(jié)序列。字符串可以包含任意的數據,但是通常是用來包含人類可讀的文本。
len()返回字符串字節(jié)數目(不是rune數)。
通過索引可以訪問某個字節(jié)值,0 <= index < len(str)。越界會panic。索引不是對應的字符而是對應的字節(jié),因為有有非ASCII的UTF8字符有多個字節(jié)。
s := "hello, world"
fmt.Println(len(s)) // "12" 英文字符占一個字節(jié)
fmt.Println(s[0], s[7]) // "104 119" ('h' and 'w')
for range
循環(huán)是循環(huán)的字節(jié),而非字符
for i, r := range "Hello, 世界?" {
fmt.Printf("%d\t%q\t%d\n", i, r, r)
}
0 'H' 72
1 'e' 101
2 'l' 108
3 'l' 108
4 'o' 111
5 ',' 44
6 ' ' 32
7 '世' 19990
10 '界' 30028
13 '?' 43088
第三列是字符的碼點。
字符串截取與鏈接
fmt.Println(s[:5]) // "hello"
fmt.Println(s[7:]) // "world"
fmt.Println(s[:]) // "hello, world"
fmt.Println("hi" + s[5:]) //hi world
比較
1 字符串可以用==和<進行比較。通過逐個字節(jié)比較完成的,因此比較的結果是字符串自然編碼的順序。
2 原生字符
使用`反引號括起來,沒有轉義操作。
應用:HTML模板、JSON面值、命令行提示信息等。
編碼
1 Unicode讓我們可以通過Unicode碼點輸入特殊的字符。有兩種形式:\uhhhh對應16bit的碼點值,\Uhhhhhhhh對應32bit的碼點值,其中h是一個十六進制數字,每一個對應碼點的UTF8編碼。以下表示相同字符:
"世界"
"\xe4\xb8\x96\xe7\x95\x8c"
"\u4e16\u754c"
"\U00004e16\U0000754c"
2 對于小于256碼點值可以寫在一個十六進制轉義字節(jié)中,例如'\x41'對應字符'A',但是對于更大的碼點則必須使用\u或\U轉義形式。因此,'\xe4\xb8\x96'并不是一個合法的rune字符,雖然 這三個字節(jié)對應一個有效的UTF8編碼的碼點。
3 字符串長度用utf8.RuneCountInString(s)來獲取。
rune
1 Unicode碼點對應Go語言中的rune整數類型。
2 因為 rune大小一致,所以支持數組索引和方便切割。
string與[]rune轉換
r := []rune("你好 world!")
fmt.Printf("%x\n", r) // "[4f60 597d 20 77 6f 72 6c 64 21]"
fmt.Println(string(r)) // "你好 world"
}
fmt.Println(string(65)) // "A", not "65" 整形字符串輸出為unicode碼點的utf8字符串。
fmt.Println(string(0x4eac)) // "京"
對字符串操作的4個包bytes、strings、strconv、unicode包
- bytes包操作[]byte。因為字符串是只讀的,因此逐步構創(chuàng)建字符串會導致很多分配和復制。使用 bytes.Buffer類型會更高。
- strings包提供切割,索引,前綴,查找替換等功能。
- strconv包提供了布爾型、整型數、浮點數和對應字符串的相互轉換,還提供了雙引號轉義相 關的轉換。
- unicode包提供了IsDigit、IsLetter、IsUpper和IsLower等類似功能,它們用于給字符分類。
字符串與數字轉換
將一個整數轉為字符串
x := 123
fmt.Println(strconv.Itoa(x)) // "123"
將一個字符串解析為整數
x, err := strconv.Atoi("123") // x is an int
y, err := strconv.ParseInt("123", 10, 64)
FormatInt和FormatUint函數可以用不同的進制來格式化數字:
fmt.Println(strconv.FormatInt(int64(23), 2)) //將int64轉換成2進制
底層原型及編碼
#runtime/string.go
type stringStruct struct {
str unsafe.Pointer
len int
}
從字符串定義可以看出字符串是一個結構體,包含字符串指針和長度。
測試代碼見下方:
package main
var s string
func main() {
s = "123 你好 world!"
}
編譯及通過gdb查看變量s的內存數據分布見圖1:

從上圖可得知字符串數字123占3個字節(jié)分別為0x31 0x32 0x33。分別對應的是ascii。
"\344\275\240\345\245\275"是8進制表示的你好。
"0xe4 0xbd 0xa0 0xe5 0xa5 0xbd"是16進制表示的你好。
那計算機是如何識別是ascii還是unicode的呢,內存中存儲的都是以字節(jié)為單元的,相鄰哪幾個是組成一個漢字呢?為了說明這個問題還是看一下上圖1以”好“這個字說明,見下表格:
| 8進制 | 345 | 245 | 275 |
|---|---|---|---|
| 16進制 | 0xe5 | 0xa5 | 0xbd |
| 2進制 | 11100101 | 10100101 | 10111101 |
“好”字的依據圖2可知,unicode的十六進制值為\u597d。
參考:https://www.unicode.org/charts/PDF/Unicode-5.2/U52-4E00.pdf

那如何將3個字節(jié)轉換成unicode的呢?
根據utf8編碼規(guī)則見圖3-go語言圣經截圖:

發(fā)現(xiàn)好字的2進制表示正好符合1110xxxx 10xxxxxx 10xxxxxx
所以計算機識別的時候只要識別到1110且后兩個字節(jié)的前2位都是10那這3個字節(jié)組成的就表示成一個字。
具體如何將這3個字節(jié)轉換成unicode,有興趣的朋友可以查查。
轉換成unicode之后就可以根據unicode碼找到字體包中的字。
參考
go語言圣經
【utf-8 Wikipedia】
Unicode官網