日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

《快学 Go 语言》第 7 课 —— 冰糖葫芦串

發(fā)布時(shí)間:2025/3/8 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《快学 Go 语言》第 7 课 —— 冰糖葫芦串 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

字符串通常有兩種設(shè)計(jì),一種是「字符」串,一種是「字節(jié)」串。「字符」串中的每個(gè)字都是定長(zhǎng)的,而「字節(jié)」串中每個(gè)字是不定長(zhǎng)的。Go 語(yǔ)言里的字符串是「字節(jié)」串,英文字符占用 1 個(gè)字節(jié),非英文字符占多個(gè)字節(jié)。這意味著無(wú)法通過(guò)位置來(lái)快速定位出一個(gè)完整的字符來(lái),而必須通過(guò)遍歷的方式來(lái)逐個(gè)獲取單個(gè)字符。

我們所說(shuō)的字符通常是指 unicode 字符,你可以認(rèn)為所有的英文和漢字在 unicode 字符集中都有一個(gè)唯一的整數(shù)編號(hào),一個(gè) unicode 通常用 4 個(gè)字節(jié)來(lái)表示,對(duì)應(yīng)的 Go 語(yǔ)言中的字符 rune 占 4 個(gè)字節(jié)。在 Go 語(yǔ)言的源碼中可以找到下面這行代碼,rune 類型是一個(gè)衍生類型,它在內(nèi)存里面使用 int32 類型的 4 個(gè)字節(jié)存儲(chǔ)。

type rune int32


使用「字符」串來(lái)表示字符串勢(shì)必會(huì)浪費(fèi)空間,因?yàn)樗械挠⑽淖址緛?lái)只需要 1 個(gè)字節(jié)來(lái)表示,用 rune 字符來(lái)表示的話那么剩余的 3 個(gè)字節(jié)都是零。但是「字符」串有一個(gè)好處,那就是可以快速定位。

為了進(jìn)一步方便讀者理解字節(jié) byte 和 字符 rune 的關(guān)系,我花了下面這張圖

圖片

其中 codepoint 是每個(gè)「字」的其實(shí)偏移量。Go 語(yǔ)言的字符串采用 utf8 編碼,中文漢字通常需要占用 3 個(gè)字節(jié),英文只需要 1 個(gè)字節(jié)。len() 函數(shù)得到的是字節(jié)的數(shù)量,通過(guò)下標(biāo)來(lái)訪問(wèn)字符串得到的是「字節(jié)」。

按字節(jié)遍歷

字符串可以通過(guò)下標(biāo)來(lái)訪問(wèn)內(nèi)部字節(jié)數(shù)組具體位置上的字節(jié),字節(jié)是 byte 類型

package main

import "fmt"

func main() {
var s = "嘻哈china"
for i:=0;i<len(s);i++ {
fmt.Printf("%x ", s[i])
}

}

-----------
e5 98 bb e5 93 88 63 68 69 6e 61

按字符 rune 遍歷

package main

import "fmt"

func main() {
var s = "嘻哈china"
for codepoint, runeValue := range s {
fmt.Printf("%d %d ", codepoint, int32(runeValue))
}
}

-----------
0 22075 3 21704 6 99 7 104 8 105 9 110 10 97


對(duì)字符串進(jìn)行 range 遍歷,每次迭代出兩個(gè)變量 codepoint 和 runeValue。codepoint 表示字符起始位置,runeValue 表示對(duì)應(yīng)的 unicode 編碼(類型是 rune)。

字節(jié)串的內(nèi)存表示

如果字符串僅僅是字節(jié)數(shù)組,那字符串的長(zhǎng)度信息是怎么得到呢?要是字符串都是字面量的話,長(zhǎng)度尚可以在編譯期計(jì)算出來(lái),但是如果字符串是運(yùn)行時(shí)構(gòu)造的,那長(zhǎng)度又是如何得到的呢?

var s1 = "hello" // 靜態(tài)字面量
var s2 = ""
for i:=0;i<10;i++ {
s2 += s1 // 動(dòng)態(tài)構(gòu)造
}
fmt.Println(len(s1))
fmt.Println(len(s2))


為解釋這點(diǎn),就必須了解字符串的內(nèi)存結(jié)構(gòu),它不僅僅是前面提到的那個(gè)字節(jié)數(shù)組,編譯器還為它分配了頭部字段來(lái)存儲(chǔ)長(zhǎng)度信息和指向底層字節(jié)數(shù)組的指針,圖示如下,結(jié)構(gòu)非常類似于切片,區(qū)別是頭部少了一個(gè)容量字段。

圖片

當(dāng)我們將一個(gè)字符串變量賦值給另一個(gè)字符串變量時(shí),底層的字節(jié)數(shù)組是共享的,它只是淺拷貝了頭部字段。

字符串是只讀的

你可以使用下標(biāo)來(lái)讀取字符串指定位置的字節(jié),但是你無(wú)法修改這個(gè)位置上的字節(jié)內(nèi)容。如果你嘗試使用下標(biāo)賦值,編譯器在語(yǔ)法上直接拒絕你。

package main

func main() {
var s = "hello"
s[0] = 'H'
}
--------
./main.go:5:7: cannot assign to s[0]

切割切割

字符串在內(nèi)存形式上比較接近于切片,它也可以像切片一樣進(jìn)行切割來(lái)獲取子串。子串和母串共享底層字節(jié)數(shù)組。

package main

import "fmt"

func main() {
var s1 = "hello world"
var s2 = s1[3:8]
fmt.Println(s2)
}

-------
lo wo

字節(jié)切片和字符串的相互轉(zhuǎn)換

在使用 Go 語(yǔ)言進(jìn)行網(wǎng)絡(luò)編程時(shí),經(jīng)常需要將來(lái)自網(wǎng)絡(luò)的字節(jié)流轉(zhuǎn)換成內(nèi)存字符串,同時(shí)也需要將內(nèi)存字符串轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)流。Go 語(yǔ)言直接內(nèi)置了字節(jié)切片和字符串的相互轉(zhuǎn)換語(yǔ)法。

package main

import "fmt"

func main() {
var s1 = "hello world"
var b = []byte(s1) // 字符串轉(zhuǎn)字節(jié)切片
var s2 = string(b) // 字節(jié)切片轉(zhuǎn)字符串
fmt.Println(b)
fmt.Println(s2)
}

--------
[104 101 108 108 111 32 119 111 114 108 100]
hello world

從節(jié)省內(nèi)存的角度出發(fā),你可能會(huì)認(rèn)為字節(jié)切片和字符串的底層字節(jié)數(shù)組是共享的。但是事實(shí)不是這樣的,底層字節(jié)數(shù)組會(huì)被拷貝。如果內(nèi)容很大,那么轉(zhuǎn)換操作是需要一定成本的。

那為什么需要拷貝呢?因?yàn)樽止?jié)切片的底層數(shù)組內(nèi)容是可以修改的,而字符串的底層字節(jié)數(shù)組是只讀的,如果共享了,就會(huì)導(dǎo)致字符串的只讀屬性不再成立。


原文發(fā)布時(shí)間為: 2018-11-20
本文作者:碼洞
本文來(lái)自云棲社區(qū)合作伙伴“碼洞”,了解相關(guān)信息可以關(guān)注“碼洞”。

總結(jié)

以上是生活随笔為你收集整理的《快学 Go 语言》第 7 课 —— 冰糖葫芦串的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。