一個(gè)字符串是一個(gè)不可改變的字節(jié)序列投储。字符串可以包含任意的數(shù)據(jù),但是通常是用來(lái)包含人類(lèi)可讀的文本阔馋。
len()返回字符串字節(jié)數(shù)目(不是rune數(shù))玛荞。
通過(guò)索引可以訪問(wèn)某個(gè)字節(jié)值,0 <= index < len(str)垦缅。越界會(huì)panic冲泥。索引不是對(duì)應(yīng)的字符而是對(duì)應(yīng)的字節(jié),因?yàn)橛杏蟹茿SCII的UTF8字符有多個(gè)字節(jié)壁涎。
s := "hello, world"
fmt.Println(len(s)) // "12" 英文字符占一個(gè)字節(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
第三列是字符的碼點(diǎn)志秃。
字符串截取與鏈接
fmt.Println(s[:5]) // "hello"
fmt.Println(s[7:]) // "world"
fmt.Println(s[:]) // "hello, world"
fmt.Println("hi" + s[5:]) //hi world
比較
1 字符串可以用==和<進(jìn)行比較怔球。通過(guò)逐個(gè)字節(jié)比較完成的,因此比較的結(jié)果是字符串自然編碼的順序浮还。
2 原生字符
使用`反引號(hào)括起來(lái)竟坛,沒(méi)有轉(zhuǎn)義操作。
應(yīng)用:HTML模板、JSON面值担汤、命令行提示信息等涎跨。
編碼
1 Unicode讓我們可以通過(guò)Unicode碼點(diǎn)輸入特殊的字符。有兩種形式:\uhhhh對(duì)應(yīng)16bit的碼點(diǎn)值崭歧,\Uhhhhhhhh對(duì)應(yīng)32bit的碼點(diǎn)值隅很,其中h是一個(gè)十六進(jìn)制數(shù)字,每一個(gè)對(duì)應(yīng)碼點(diǎn)的UTF8編碼率碾。以下表示相同字符:
"世界"
"\xe4\xb8\x96\xe7\x95\x8c"
"\u4e16\u754c"
"\U00004e16\U0000754c"
2 對(duì)于小于256碼點(diǎn)值可以寫(xiě)在一個(gè)十六進(jìn)制轉(zhuǎn)義字節(jié)中叔营,例如'\x41'對(duì)應(yīng)字符'A',但是對(duì)于更大的碼點(diǎn)則必須使用\u或\U轉(zhuǎn)義形式所宰。因此绒尊,'\xe4\xb8\x96'并不是一個(gè)合法的rune字符,雖然 這三個(gè)字節(jié)對(duì)應(yīng)一個(gè)有效的UTF8編碼的碼點(diǎn)仔粥。
3 字符串長(zhǎng)度用utf8.RuneCountInString(s)來(lái)獲取婴谱。
rune
1 Unicode碼點(diǎn)對(duì)應(yīng)Go語(yǔ)言中的rune整數(shù)類(lèi)型。
2 因?yàn)?rune大小一致躯泰,所以支持?jǐn)?shù)組索引和方便切割勘究。
string與[]rune轉(zhuǎn)換
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碼點(diǎn)的utf8字符串。
fmt.Println(string(0x4eac)) // "京"
對(duì)字符串操作的4個(gè)包bytes斟冕、strings口糕、strconv、unicode包
- bytes包操作[]byte磕蛇。因?yàn)樽址侵蛔x的景描,因此逐步構(gòu)創(chuàng)建字符串會(huì)導(dǎo)致很多分配和復(fù)制。使用 bytes.Buffer類(lèi)型會(huì)更高秀撇。
- strings包提供切割超棺,索引,前綴,查找替換等功能呵燕。
- strconv包提供了布爾型棠绘、整型數(shù)、浮點(diǎn)數(shù)和對(duì)應(yīng)字符串的相互轉(zhuǎn)換再扭,還提供了雙引號(hào)轉(zhuǎn)義相 關(guān)的轉(zhuǎn)換氧苍。
- unicode包提供了IsDigit、IsLetter泛范、IsUpper和IsLower等類(lèi)似功能让虐,它們用于給字符分類(lèi)。
字符串與數(shù)字轉(zhuǎn)換
將一個(gè)整數(shù)轉(zhuǎn)為字符串
x := 123
fmt.Println(strconv.Itoa(x)) // "123"
將一個(gè)字符串解析為整數(shù)
x, err := strconv.Atoi("123") // x is an int
y, err := strconv.ParseInt("123", 10, 64)
FormatInt和FormatUint函數(shù)可以用不同的進(jìn)制來(lái)格式化數(shù)字:
fmt.Println(strconv.FormatInt(int64(23), 2)) //將int64轉(zhuǎn)換成2進(jìn)制
底層原型及編碼
#runtime/string.go
type stringStruct struct {
str unsafe.Pointer
len int
}
從字符串定義可以看出字符串是一個(gè)結(jié)構(gòu)體罢荡,包含字符串指針和長(zhǎng)度赡突。
測(cè)試代碼見(jiàn)下方:
package main
var s string
func main() {
s = "123 你好 world!"
}
編譯及通過(guò)gdb查看變量s的內(nèi)存數(shù)據(jù)分布見(jiàn)圖1:
從上圖可得知字符串?dāng)?shù)字123占3個(gè)字節(jié)分別為0x31 0x32 0x33对扶。分別對(duì)應(yīng)的是ascii。
"\344\275\240\345\245\275"是8進(jìn)制表示的你好惭缰。
"0xe4 0xbd 0xa0 0xe5 0xa5 0xbd"是16進(jìn)制表示的你好浪南。
那計(jì)算機(jī)是如何識(shí)別是ascii還是unicode的呢,內(nèi)存中存儲(chǔ)的都是以字節(jié)為單元的漱受,相鄰哪幾個(gè)是組成一個(gè)漢字呢络凿?為了說(shuō)明這個(gè)問(wèn)題還是看一下上圖1以”好“這個(gè)字說(shuō)明,見(jiàn)下表格:
8進(jìn)制 | 345 | 245 | 275 |
---|---|---|---|
16進(jìn)制 | 0xe5 | 0xa5 | 0xbd |
2進(jìn)制 | 11100101 | 10100101 | 10111101 |
“好”字的依據(jù)圖2可知拜效,unicode的十六進(jìn)制值為\u597d喷众。
參考:https://www.unicode.org/charts/PDF/Unicode-5.2/U52-4E00.pdf
那如何將3個(gè)字節(jié)轉(zhuǎn)換成unicode的呢?
根據(jù)utf8編碼規(guī)則見(jiàn)圖3-go語(yǔ)言圣經(jīng)截圖:
發(fā)現(xiàn)好字的2進(jìn)制表示正好符合1110xxxx 10xxxxxx 10xxxxxx
所以計(jì)算機(jī)識(shí)別的時(shí)候只要識(shí)別到1110且后兩個(gè)字節(jié)的前2位都是10那這3個(gè)字節(jié)組成的就表示成一個(gè)字紧憾。
具體如何將這3個(gè)字節(jié)轉(zhuǎn)換成unicode到千,有興趣的朋友可以查查。
轉(zhuǎn)換成unicode之后就可以根據(jù)unicode碼找到字體包中的字赴穗。
參考
go語(yǔ)言圣經(jīng)
【utf-8 Wikipedia】
Unicode官網(wǎng)