unicode
請先查看 字符編碼筆記: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編碼,并且字符串能夠存儲任何字節(jié)
- 沒有經(jīng)過字節(jié)級別的轉(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),底層的存儲用UTF-8編碼
通過unicode部分我們已經(jīng)知道惋啃,UTF-8編碼方案會把一個(gè)Unicode字符編碼為一個(gè)長度在[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)義成單引號圍繞的字符字面值边灭,%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è)元素分別對應(yīng)[]byte中的后九個(gè)元素绒瘦,其中連續(xù)的三個(gè)對應(yīng)[]rune中的一個(gè)称簿,因?yàn)閞une中的中文字符使用UTF-8編碼,占用三個(gè)字節(jié)椭坚,同時(shí)也是UTF-8編碼對應(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
以'愛'為例予跌,其對應(yīng)的Unicode編碼是16進(jìn)制的7231
,對應(yīng)二進(jìn)制111001000110001
善茎, 那怎么對應(yīng)上[]byte中的e7 88 b1
呢券册?
這里有一個(gè)轉(zhuǎn)換過程,如下:
- 因?yàn)?x010000> 0x7231 > 0x07FF垂涯,所以套用UTF-8的模板是
1110xxxx 10xxxxxx 10xxxxxx
- 根據(jù)模板中x數(shù)量對特殊字符的高位補(bǔ)0烁焙。x的數(shù)量是16,所以需要對
111001000110001
的高位補(bǔ)1個(gè)0耕赘,此時(shí)特殊字符的二進(jìn)制表示為:0111001000110001
- 模板中包含x的有3個(gè)部分骄蝇,且長度分別是4、6和6操骡,所以
0111001000110001
由底位向高位分別截取6九火、6和4位,得到110001
册招、001000
和0111
- 將得到的截取位依次填充至模板岔激,可得到UTF-8的完整二進(jìn)制序列為:
11100111 10001000 10110001
,也就對應(yīng)上了e7 88 b1
整數(shù)轉(zhuǎn)string
通過上面已知是掰,'愛'的Unicode編碼對應(yīng)16進(jìn)制是7231
虑鼎,換算成整數(shù)就是29233
fmt.Printf("%q\n", 29233)
// output
'愛'
所以我們?nèi)绻麑φ麛?shù)進(jìn)行強(qiáng)制轉(zhuǎn)換成string,需要注意是否是對應(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語句會先把被遍歷的字符串值拆成一個(gè)字節(jié)序列絮短,然后再試圖找出這個(gè)字節(jié)序列中包含的每一個(gè)UTF-8編碼值江兢,或者說每一個(gè)Unicode字符