golang中的string、編碼

一個字符串是一個不可改變的字節(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:

圖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

圖2

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容