go之UTF-8

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位,得到1100010010000111
  • 將得到的截取位依次填充至模板,可得到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字符

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

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容