unicode
請(qǐng)先查看 字符編碼筆記:ASCII,Unicode 和 UTF-8
go的字符串是如何編碼的
根據(jù)golang官方博客https://blog.golang.org/strings的原文:
Go source code is always UTF-8.
A string holds arbitrary bytes.
A string literal, absent byte-level escapes, always holds valid UTF-8 sequences.
大致意思如下:
- go中的代碼總是用UTF-8編碼,并且字符串能夠存儲(chǔ)任何字節(jié)
- 沒有經(jīng)過字節(jié)級(jí)別的轉(zhuǎn)義,那么字符串是一個(gè)標(biāo)準(zhǔn)的utf8序列
byte和rune
在Go語言中,一個(gè)string類型的值既可以被拆分為一個(gè)包含多個(gè)字符的序列([]rune),也可以被拆分為一個(gè)包含多個(gè)字節(jié)的序列([]byte)
rune是Go語言特有的一個(gè)基本數(shù)據(jù)類型,它的一個(gè)值就代表一個(gè)字符,即:一個(gè)Unicode字符。比如,'G'、'o'、'愛'、'好'、'者'代表的就都是一個(gè)個(gè)Unicode字符。rune的底層表達(dá)使用的是Unicode代碼點(diǎn),底層的存儲(chǔ)用UTF-8編碼
通過unicode部分我們已經(jīng)知道,UTF-8編碼方案會(huì)把一個(gè)Unicode字符編碼為一個(gè)長(zhǎng)度在[1, 4]范圍內(nèi)的字節(jié)序列。所以rune類型的值也可以由一個(gè)或多個(gè)字節(jié)來代表。
type rune = int32 //rune實(shí)際上就是int32類型的一個(gè)別名類型
舉幾個(gè)栗子
string轉(zhuǎn)[]byte/[]rune
str := "Go愛好者"
fmt.Printf(" => runes(char): %q\n", []rune(str))
fmt.Printf(" => runes(hex): %x\n", []rune(str))
fmt.Printf(" => bytes(hex): [% x]\n", []byte(str))
// output
=> runes(char): ['G' 'o' '愛' '好' '者'] //這里用%q來安全的轉(zhuǎn)義成單引號(hào)圍繞的字符字面值,%s無法轉(zhuǎn)義成字符字面值
=> runes(hex): [47 6f 7231 597d 8005]
=> bytes(hex): [47 6f e7 88 b1 e5 a5 bd e8 80 85]
分析:
- []rune中的前兩個(gè)元素即47和6f與[]byte中的前兩個(gè)元素是一致的,因?yàn)閁TF-8是兼容ASCII的,都用一個(gè)字節(jié)表示
- []rune中的后三個(gè)元素分別對(duì)應(yīng)[]byte中的后九個(gè)元素,其中連續(xù)的三個(gè)對(duì)應(yīng)[]rune中的一個(gè),因?yàn)閞une中的中文字符使用UTF-8編碼,占用三個(gè)字節(jié),同時(shí)也是UTF-8編碼對(duì)應(yīng)Unicode碼點(diǎn)
如下是Unicode編碼中UTF-8的編碼規(guī)則
Unicode編碼(十六進(jìn)制) UTF-8 字節(jié)流(二進(jìn)制)
000000-00007F 0xxxxxxx
000080-0007FF 110xxxxx 10xxxxxx
000800-00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000-10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
以'愛'為例,其對(duì)應(yīng)的Unicode編碼是16進(jìn)制的7231,對(duì)應(yīng)二進(jìn)制111001000110001, 那怎么對(duì)應(yīng)上[]byte中的e7 88 b1呢?
這里有一個(gè)轉(zhuǎn)換過程,如下:
- 因?yàn)?x010000> 0x7231 > 0x07FF,所以套用UTF-8的模板是
1110xxxx 10xxxxxx 10xxxxxx - 根據(jù)模板中x數(shù)量對(duì)特殊字符的高位補(bǔ)0。x的數(shù)量是16,所以需要對(duì)
111001000110001的高位補(bǔ)1個(gè)0,此時(shí)特殊字符的二進(jìn)制表示為:0111001000110001 - 模板中包含x的有3個(gè)部分,且長(zhǎng)度分別是4、6和6,所以
0111001000110001由底位向高位分別截取6、6和4位,得到110001、001000和0111 - 將得到的截取位依次填充至模板,可得到UTF-8的完整二進(jìn)制序列為:
11100111 10001000 10110001,也就對(duì)應(yīng)上了e7 88 b1
整數(shù)轉(zhuǎn)string
通過上面已知,'愛'的Unicode編碼對(duì)應(yīng)16進(jìn)制是7231,換算成整數(shù)就是29233
fmt.Printf("%q\n", 29233)
// output
'愛'
所以我們?nèi)绻麑?duì)整數(shù)進(jìn)行強(qiáng)制轉(zhuǎn)換成string,需要注意是否是對(duì)應(yīng)的合法的Unicode碼點(diǎn)
str := string(235234234234)
fmt.Printf("%q\n", str)
// output
"?"
這里因?yàn)?code>235234234234不是合法的Unicode碼點(diǎn),所以強(qiáng)制轉(zhuǎn)換后的結(jié)果是非預(yù)期的
for...range處理字符串
見代碼:
str := "go,你好"
for i, v := range str {
fmt.Printf("%d %q\n", i, v)
}
// output
// 注意這里輸出的索引值是以字節(jié)為跨度計(jì)算的
0 'g'
1 'o'
2 ','
5 '你'
8 '好'
帶有range子句的for語句會(huì)先把被遍歷的字符串值拆成一個(gè)字節(jié)序列,然后再試圖找出這個(gè)字節(jié)序列中包含的每一個(gè)UTF-8編碼值,或者說每一個(gè)Unicode字符