深度解密Go語言中字符串的使用
Go 字符串實(shí)現(xiàn)原理
Go 的字符串有個(gè)特性,不管長(zhǎng)度是多少,大小都是固定的 16 字節(jié)。
package?main import?( ????"fmt" ????"unsafe" ) func?main()?{ ????fmt.Println( ????????unsafe.Sizeof("komeiji?satori"), ????)??//?16 ????fmt.Println( ????????unsafe.Sizeof("satori"), ????)??//?16 }
顯然用鼻子也能猜到原因,Go 的字符串底層并沒有實(shí)際保存這些字符,而是保存了一個(gè)指針,該指針指向的內(nèi)存區(qū)域負(fù)責(zé)存儲(chǔ)具體的字符。由于指針的大小是固定的,所以不管字符串多長(zhǎng),大小都是相等的。
另外字符串大小是 16 字節(jié),指針是 8 字節(jié),那么剩下的 8 字節(jié)是什么呢?不用想,顯然是長(zhǎng)度。下面來驗(yàn)證一下我們結(jié)論:
以上是 Go 字符串的底層結(jié)構(gòu),位于 runtime/string.go 中。字符串在底層是一個(gè)結(jié)構(gòu)體,包含兩個(gè)字段,其中 str 是一個(gè) 8 字節(jié)的萬能指針,指向一個(gè)數(shù)組,數(shù)組里面存儲(chǔ)的就是實(shí)際的字符;而 len 則表示長(zhǎng)度,也是 8 字節(jié)。
因此結(jié)構(gòu)很清晰了:
str 指向的數(shù)組里面存儲(chǔ)的就是所有的字符,并且類型是 uint8,因?yàn)?Go 的字符串默認(rèn)采用 utf-8 編碼。所以一個(gè)漢字在 Go 里面占 3 字節(jié),我們先用 Python 舉個(gè)例子:
>>>?name?=?"琪露諾" >>>?[c?for?c?in?name.encode("utf-8")] [231,?144,?170,?233,?156,?178,?232,?175,?186] >>>
那么對(duì)于 Go 而言,底層就是這么存儲(chǔ)的:
我們驗(yàn)證一下:
package?main import?"fmt" func?main()?{ ????name?:=?"琪露諾" ????//?長(zhǎng)度是?9,不是?3 ????fmt.Println(len(name))??//?9 ????//?查看底層數(shù)組存儲(chǔ)的值 ????//?可以轉(zhuǎn)成切片查看 ????fmt.Println( ????????[]byte(name), ????)??//?[231?144?170?233?156?178?232?175?186] }
結(jié)果和我們想的一樣,并且內(nèi)置函數(shù) len 在統(tǒng)計(jì)字符串長(zhǎng)度時(shí),計(jì)算的是底層數(shù)組的長(zhǎng)度。
字符串的截取
如果要截取字符串的某個(gè)子串,要怎么做呢?如果是 Python 的話很簡(jiǎn)單:
>>>?name?=?"琪露諾" >>>?name[0] '琪' >>>?name[:?2] '琪露' >>>
因?yàn)?Python 字符串里面的每個(gè)字符的大小都是相同的,可能是 1 字節(jié)、2字節(jié)、4字節(jié)。但不管是哪種,一個(gè)字符串里面的所有字符都具有相同的大小,因此才能通過索引準(zhǔn)確定位。
但在 Go 里面這種做法行不通,Go 的字符串采用 utf-8 編碼,不同字符占用的大小不同,ASCII 字符占 1 字節(jié),漢字占 3 字節(jié),所以無法通過索引準(zhǔn)確定位。
package?main import?"fmt" func?main()?{ ????name?:=?"琪露諾" ????fmt.Println( ????????name[0],?name[1],?name[2], ????)??//?231?144?170 ????fmt.Println(name[:?3])??//?琪 }
如果一個(gè)字符串里面既有英文又有中文,那么想通過索引準(zhǔn)確定位是不可能的。因此這個(gè)時(shí)候我們需要進(jìn)行轉(zhuǎn)換,讓它像 Python 一樣,每個(gè)字符都具有相同的大小。
package?main import?"fmt" func?main()?{ ????name?:=?"琪露諾" ????//?rune?等價(jià)于?int32 ????//?此時(shí)每個(gè)元素統(tǒng)一占?4?字節(jié) ????//?并且?[]rune(name)?的長(zhǎng)度才是字符串的字符個(gè)數(shù) ????fmt.Println( ????????[]rune(name), ????)?//?[29738?38706?35834] ????//?然后再進(jìn)行截取 ????fmt.Println( ????????string([]rune(name)[0]), ????????string([]rune(name)[:?2]), ????)??//?琪?琪露 }
所以對(duì)于字符串 "憨pi" 而言,如果是 utf-8 存儲(chǔ),那么只需要 5 個(gè)字節(jié)。但很明顯,基于索引查找指定的字符是不可能的,除非事先知道字符串長(zhǎng)什么樣子。如果是轉(zhuǎn)成 []rune 的話,那么需要 12 字節(jié)存儲(chǔ),內(nèi)存占用變大了,但可以很方便地查找某個(gè)字符或者某個(gè)子串。
字符串和切片的轉(zhuǎn)換
字符串和切片之間是可以互轉(zhuǎn)的,但切片只能是 uint8 或者 int32 類型,另外 uint8 也可以寫成 byte,int32 可以寫成 rune。
由于 byte 是 1 字節(jié),那么當(dāng)字符串包含漢字,轉(zhuǎn)成 []byte 切片時(shí),一個(gè)漢字需要 3 個(gè)byte 表示。因此字符串 "憨pi" 轉(zhuǎn)成 []byte 之后,長(zhǎng)度為 5。
而 rune 是 4 字節(jié),可以容納所有的字符,那么轉(zhuǎn)成 []rune 切片時(shí),不管什么字符,都只需要一個(gè) rune 表示即可。所以字符串 "憨pi" 轉(zhuǎn)成 []rune 之后,長(zhǎng)度為 3。
因此當(dāng)你想統(tǒng)計(jì)字符串的字符個(gè)數(shù)時(shí),最好轉(zhuǎn)成 []rune 數(shù)組之后再統(tǒng)計(jì)。如果是字節(jié)個(gè)數(shù),那么直接使用內(nèi)置函數(shù) len 即可。
我們舉例說明,先來看一段 Python 代碼:
>>>?s?=?"憨pi" #?采用utf-8編碼(等價(jià)于Go的[]byte數(shù)組) #?"憨"?需要?230?134?168?三個(gè)整數(shù)來表示 #?而?"p"?和?"i"?均只需?1?個(gè)字節(jié),分別為112和105 >>>?[c?for?c?in?s.encode("utf-8")] [230,?134,?168,?112,?105] #?采用?unicode?編碼(類似于Go的[]rune數(shù)組) #?所有字符都只需要1個(gè)整數(shù)表示 #?但對(duì)于ASCII字符而言,不管什么編碼,對(duì)應(yīng)的數(shù)值不變 >>>?[ord(c)?for?c?in?s] [25000,?112,?105]
我們用 Go 再演示一下:
package?main import?"fmt" func?main()?{ ????s?:=?"憨pi" ????fmt.Println( ????????[]byte(s), ????)?//?[230?134?168?112?105] ????fmt.Println( ????????[]rune(s), ????)??//?[25000?112?105] }
結(jié)果是一樣的,當(dāng)然這個(gè)過程我們也可以反向進(jìn)行:
package?main import?"fmt" func?main()?{ ????s1?:=?[]byte{230,?134,?168,?112,?105} ????fmt.Println(string(s1))?//?憨pi ????s2?:=?[]rune{25000,?112,?105} ????fmt.Println(string(s2))?//?憨pi }
結(jié)果沒有任何問題。
字符串和切片共享底層數(shù)組
我們知道字符串和切片內(nèi)部都有一個(gè)指針,指針指向一個(gè)數(shù)組,該數(shù)組存放具體的元素。
//?runtime/string.go type?stringStruct?struct?{ ????str?unsafe.Pointer ????len?int } //?runtime/slice.go type?slice?struct?{ ????array?unsafe.Pointer ????len???int ????cap???int }
假設(shè)有一個(gè)字符串 "abc",然后基于該字符串創(chuàng)建一個(gè)切片,那么兩者的結(jié)構(gòu)如下:
字符串在轉(zhuǎn)成切片的時(shí)候,會(huì)將底層數(shù)組也拷貝一份。那么問題來了,在基于字符串創(chuàng)建切片的時(shí)候,能不能不拷貝數(shù)組呢?也就是下面這個(gè)樣子:
如果字符串比較大,或者說需要和切片之間來回轉(zhuǎn)換的話,這種方式無疑會(huì)減少大量開銷。Go 提供了萬能指針幫我們實(shí)現(xiàn)這一點(diǎn),所以先來了解一下什么是萬能指針。
什么是萬能指針
我們知道 C 的指針不僅可以相互轉(zhuǎn)換,而且還可以參與運(yùn)算,但 Go 不行,因?yàn)?Go 的指針是類型安全的。Go 編譯器對(duì)類型的檢測(cè)非常嚴(yán)格,讓你在享受指針帶來的便利時(shí),又給指針施加了很多制約來保證安全。因此 Go 的指針不可以相互轉(zhuǎn)換,也不可以參與運(yùn)算。
但保證安全是需要以犧牲效率為代價(jià)的,如果你能保證寫出的程序就是安全的,那么可以使用 Go 中的萬能指針,從而繞過類型系統(tǒng)的檢測(cè),讓程序運(yùn)行的更快。
萬能指針在 Go 里面叫做 unsafe.Pointer,它位于 unsafe 包下面。當(dāng)然這個(gè)包名看起來有點(diǎn)怪怪的,因?yàn)檫@個(gè)包可以讓我們繞過 Go 類型系統(tǒng)的檢測(cè),直接訪問內(nèi)存,從而提升效率。所以它有點(diǎn)危險(xiǎn),而 Go 官方也不推薦開發(fā)者使用,于是起了這個(gè)名字。
但實(shí)際上 unsafe 包在底層被大量使用,所以不要被名字誤導(dǎo)了,這個(gè)包是一定要掌握的。
回到萬能指針上面來,Go 的指針不可以相互轉(zhuǎn)換,但是它們都可以和萬能指針轉(zhuǎn)換。舉個(gè)例子:
package?main import?( ????"fmt" ????"unsafe" ) func?main()?{ ????//?一個(gè)?[]int8?類型的切片 ????s1?:=?[]int8{1,?2,?3,?4} ????//?如果直接轉(zhuǎn)成?[]int16?是會(huì)報(bào)錯(cuò)的 ????//?因?yàn)?Go?的類型系統(tǒng)不允許這么做 ????//?但是有萬能指針,任何指針都可以和它轉(zhuǎn)換 ????//?我們可以先將?s1?的指針轉(zhuǎn)成萬能指針 ????//?然后再將萬能指針轉(zhuǎn)成?*[]int16,最后再解引用 ????s2?:=?*(*[]int16)(unsafe.Pointer(&s1)) ????//?那么問題來了,指針雖然轉(zhuǎn)換了 ????//?但是內(nèi)存地址沒變,內(nèi)存里的值也沒變 ????//?由于?s2?是?[]int16?類型,s1?是?[]int8?類型 ????//?所以它會(huì)把?s1[0]?和?s1[1]?整體作為?s2[0] ????//?會(huì)把?s1[2]?和?s1[3]?整體作為?s2[1] ????fmt.Println(s2)??//?[513?1027?0?0] ???? ????//?int8?類型的?1?和?2?組合成?int16? ????//?int8?類型的?3?和?4?組合成?int16? ????fmt.Println(2?<<?8?+?1)??//?513 ????fmt.Println(4?<<?8?+?3)??//?1027 }
因此把 Go 的萬能指針想象成 C 的空指針 void * 即可。
那么讓字符串和切片共享數(shù)組,我們就可以這么做:
package?main import?( ????"fmt" ????"unsafe" ) func?main()?{ ????str?:=?"abc" ????slice?:=?*(*[]byte)(unsafe.Pointer(&str)) ????fmt.Println(slice)??//?[97?98?99] ????fmt.Println(cap(slice))??//?10036576 }
雖然轉(zhuǎn)換成功了,但是還有點(diǎn)問題,容量不太對(duì)勁。至于原因也很簡(jiǎn)單,字符串和切片在底層都是結(jié)構(gòu)體,并且它們的前兩個(gè)字段相同,所以轉(zhuǎn)換之后打印沒有問題。但字符串沒有容量的概念,它是定長(zhǎng)的,所以轉(zhuǎn)成切片的時(shí)候 cap 就丟失了,打印的就是亂七八糟的值。
所以我們需要再完善一下:
package?main import?( ????"fmt" ????"unsafe" ) func?StringToBytes(s?string)?[]byte { ????//?既然字符串轉(zhuǎn)切片,會(huì)丟失容量 ????//?那么加上去就好了,做法也很簡(jiǎn)單 ????//?新建一個(gè)結(jié)構(gòu)體,將容量(等于長(zhǎng)度)加進(jìn)去 ????return?*(*[]byte)(unsafe.Pointer( ????????&struct?{ ????????????string ????????????Cap?int ????????}{s,?len(s)}, ????)) } func?BytesToString(b?[]byte)?string { ????//?切片轉(zhuǎn)字符串就簡(jiǎn)單了,直接轉(zhuǎn)即可 ????//?轉(zhuǎn)的過程中,切片的?Cap?字段會(huì)丟棄 ????return?*(*string)(unsafe.Pointer(&b)) } func?main()?{ ????fmt.Println( ????????StringToBytes("abc"), ????)?//?[97?98?99] ????fmt.Println( ????????BytesToString([]byte{97,?98,?99}), ????)?//?abc }
結(jié)果沒有問題,但我們?cè)趺醋C明它們是共享數(shù)組的呢?很簡(jiǎn)單:
package?main import?( ????"fmt" ????"unsafe" ) func?main()?{ ????slice?:=?[]byte{97,?98,?99} ????str?:=?*(*string)(unsafe.Pointer(&slice)) ????fmt.Println(str)??//?abc ????slice[0]?=?'A' ????fmt.Println(str)??//?Abc }
操作切片等于操作底層數(shù)組,而 str 前后的打印結(jié)果不一致,所以確實(shí)是共享同一個(gè)數(shù)組。但需要注意的是,這里是先創(chuàng)建的切片,因此底層數(shù)組是可以修改的,沒有問題。
但如果創(chuàng)建的是字符串,然后基于字符串得到切片,那么切片就不可以修改了。因?yàn)樽址遣豢尚薷牡?,所以底層?shù)組也不可修改,也意味著切片不可以修改。
字符串和其它數(shù)據(jù)結(jié)構(gòu)的轉(zhuǎn)化
以上我們就介紹完了字符串的原理,再來看看工作中一些常見的字符串操作。
整數(shù)和字符串相互轉(zhuǎn)換
如果想把一個(gè)整數(shù)轉(zhuǎn)成字符串,那么該怎做呢?比如將 97 轉(zhuǎn)成字符串。有過 Python 經(jīng)驗(yàn)的,應(yīng)該下意識(shí)會(huì)想到 string(97),但這是不行的,它返回的是字符串 "a",因?yàn)?97 對(duì)應(yīng)的字符是 'a'。
如果將整數(shù)轉(zhuǎn)成字符串,應(yīng)該使用 strconv 包下的 Itoa 函數(shù),這個(gè)和 C 語言類似。
package?main import?( ????"fmt" ????"strconv" ) func?main()?{ ????fmt.Println(strconv.Itoa(97)) ????fmt.Println(strconv.Itoa(97)?==?"97") ????/* ???????97 ???????true ????*/ ????//?同理,將字符串轉(zhuǎn)成整數(shù)則是?Atoi ????s?:=?"97" ????if?num,?err?:=?strconv.Atoi(s);?err?!=?nil?{ ????????fmt.Println(err) ????}?else?{ ????????fmt.Println(num?==?97)?//?true ????} ????s?=?"97xx"? ????if?num,?err?:=?strconv.Atoi(s);?err?!=?nil?{ ????????fmt.Println( ????????????err, ????????)??//?strconv.Atoi:?parsing?"97xx":?invalid?syntax ????}?else?{ ????????fmt.Println(num) ????} }
Atoi 和 Itoa 專門用于整數(shù)和字符串之間的轉(zhuǎn)換,strconv 這個(gè)包還提供了 Format 系列和 Parse 系列的函數(shù),用于其它數(shù)據(jù)結(jié)構(gòu)和字符串之間的轉(zhuǎn)換,當(dāng)然里面也包括整數(shù)。
Parse 系列函數(shù)
Parse 一類函數(shù)用于轉(zhuǎn)換字符串為給定類型的值。
ParseBool
將指定字符串轉(zhuǎn)換為對(duì)應(yīng)的bool類型,只接受 1、0、t、f、T、F、true、false、True、False、TRUE、FALSE,否則返回錯(cuò)誤;
package?main import?( ????"fmt" ????"strconv" ) func?main()?{ ????//因?yàn)樽址D(zhuǎn)換時(shí)可能發(fā)生失敗,因此都會(huì)帶一個(gè)error ????//而這里解析成功了,所以?error?是?nil ????fmt.Println(strconv.ParseBool("1"))??//?true?<nil> ????fmt.Println(strconv.ParseBool("F"))?//?false?<nil> }
ParseInt
函數(shù)原型:func ParseInt(s string, base int, bitSize int) (i int64, err error)
- s:轉(zhuǎn)成 int 的字符串;
- base:指定進(jìn)制(2 到 36),如果 base 為 0,那么會(huì)從字符串的前綴來判斷,如 0x 表示 16 進(jìn)制等等,如果前綴也沒有那么默認(rèn)是 10 進(jìn)制;
- bistSize:整數(shù)類型,0、8、16、32、64 分別代表 int、int8、int16、int32、int64;
返回的 err 是 *NumErr 類型,如果語法有誤,err.Error = ErrSyntax;如果結(jié)果超出范圍,err.Error = ErrRange。
package?main import?( ????"fmt" ????"strconv" ) func?main()?{ ????fmt.Println( ????????strconv.ParseInt("0x16",?0,?0), ????)??//?22?<nil> ????fmt.Println( ????????strconv.ParseInt("16",?16,?0), ????)??//?22?<nil> ????fmt.Println( ????????strconv.ParseInt("16",?0,?0), ????)??//?16?<nil> ????fmt.Println( ????????strconv.ParseInt("016",?0,?0),? ????)??//?14?<nil> ????//進(jìn)制為?2,但是字符串出現(xiàn)了?6,無法解析 ????fmt.Println( ????????strconv.ParseInt("16",?2,?0),? ????)?//?0?strconv.ParseInt:?parsing?"16":?invalid?syntax ????//只指定?8?位,顯然存不下 ????fmt.Println( ????????strconv.ParseInt("257",?0,?8), ????)?//?127?strconv.ParseInt:?parsing?"257":?value?out?of?range ????//還可以指定正負(fù)號(hào) ????fmt.Println( ????????strconv.ParseInt("-0x16",?0,?0), ????)?//?-22?<nil> ????fmt.Println( ????????strconv.ParseInt("-016",?0,?0), ????)??//?-14?<nil> }
ParseUint
ParseUint 類似 ParseInt,但不接受正負(fù)號(hào),用于無符號(hào)整型。
ParseFloat
函數(shù)原型:func ParseFloat(s string, bitSize int) (f float64, err error),其中 bitSize為:32、64,表示對(duì)應(yīng)精度的 float
package?main import?( ????"fmt" ????"strconv" ) func?main()?{ ????fmt.Println( ????????strconv.ParseFloat("3.14",?64),? ????)??//3.14?<nil> }
Format 系列函數(shù)
Format 系列函數(shù)就比較簡(jiǎn)單了,就是將指定類型的數(shù)據(jù)格式化成字符串,Parse 則是將字符串解析成指定數(shù)據(jù)類型,這兩個(gè)是相反的。另外轉(zhuǎn)成字符串的話,則不需要擔(dān)心 error 了。
FormatBool
package?main import?( ????"fmt" ????"strconv" ) func?main()?{ ????//?如果是?Parse?系列的話會(huì)返回兩個(gè)值,?因?yàn)榭赡軙?huì)出錯(cuò) ????//?所以多一個(gè)?error,?因此需要兩個(gè)變量來接收 ????//?而?Format?系列則無需擔(dān)心,?因?yàn)檗D(zhuǎn)成字符串是不會(huì)出錯(cuò)的 ????//?所以只返回一個(gè)值,?接收的時(shí)候只需要一個(gè)變量即可 ????fmt.Println( ????????strconv.FormatBool(true), ????)??//true ????fmt.Println( ????????strconv.FormatBool(false)?==?"false", ????)??//true }
FormatInt
傳入字符串和指定的進(jìn)制。
package?main import?( ????"fmt" ????"strconv" ) ?func?main()?{ ????//?數(shù)值是?24,但它是?16?進(jìn)制的 ????//?所以對(duì)應(yīng)成?10?進(jìn)制是?18 ????fmt.Println( ????????strconv.FormatInt(24,?16), ????)??//?18 }
FormatUint
是 FormatInt 的無符號(hào)版本,兩者差別不大。
FormatFloat
函數(shù)原型:func FormatFloat(f float64, fmt byte, prec, bitSize int) string,作用是將浮點(diǎn)數(shù)轉(zhuǎn)成為字符串并返回。
- f:浮點(diǎn)數(shù);
- fmt:表示格式,'f'(-ddd.dddd)、'b'(-ddddp±ddd,指數(shù)為二進(jìn)制)、'e'(-d.dddde±dd,十進(jìn)制指數(shù))、'E'(-d.ddddE±dd,十進(jìn)制指數(shù))、'g'(指數(shù)很大時(shí)用'e'格式,否則'f'格式)、'G'(指數(shù)很大時(shí)用'E'格式,否則'f'格式);
- prec:prec 控制精度(排除指數(shù)部分),當(dāng) fmt 為 'f'、'e'、'E',它表示小數(shù)點(diǎn)后的數(shù)字個(gè)數(shù);為 'g'、'G',它表示總的數(shù)字個(gè)數(shù)。如果 prec 為 -1,則代表使用最少數(shù)量的、但又必需的數(shù)字來表示 f;
- bitSize:f 是哪一種精度的 float,32 或者 64;
package?main import?( ????"fmt" ????"strconv" ) func?main()?{ ????fmt.Println( ????????strconv.FormatFloat(3.1415,?'f',?-1,?64)) ????fmt.Println( ????????strconv.FormatFloat(3.1415,?'e',?-1,?64)) ????fmt.Println( ????????strconv.FormatFloat(3.1415,?'E',?-1,?64)) ????fmt.Println( ????????strconv.FormatFloat(3.1415,?'g',?-1,?64)) ????/* ????3.1415 ????3.1415e+00 ????3.1415E+00 ????3.1415 ?????*/ }
小結(jié)
- 字符串底層是一個(gè)結(jié)構(gòu)體,內(nèi)部不存儲(chǔ)實(shí)際數(shù)據(jù),而是只保存一個(gè)指針和一個(gè)長(zhǎng)度;
- 字符串采用 utf-8 編碼,這種編碼的特點(diǎn)是省內(nèi)存,但是無法通過索引準(zhǔn)確定位字符和截取子串;
- 字符串可以和 []byte、[]rune 類型的切片互相轉(zhuǎn)換,特別是 []rune,如果想計(jì)算字符長(zhǎng)度或者截取子串,需要轉(zhuǎn)成 []rune;
- 字符串和切片之間可以共享底層數(shù)組,其實(shí)現(xiàn)的核心就在于萬能指針;
以上就是深度解密Go語言中字符串的使用的詳細(xì)內(nèi)容,更多關(guān)于Go語言 字符串的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用go net實(shí)現(xiàn)簡(jiǎn)單的redis通信協(xié)議
本文主要介紹了go net實(shí)現(xiàn)簡(jiǎn)單的redis通信協(xié)議,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12Golang中int類型和字符串類型相互轉(zhuǎn)換的實(shí)現(xiàn)方法
在日常開發(fā)中,經(jīng)常需要將數(shù)字轉(zhuǎn)換為字符串或者將字符串轉(zhuǎn)換為數(shù)字,在 Golang 中,有一些很簡(jiǎn)便的方法可以實(shí)現(xiàn)這個(gè)功能,接下來就詳細(xì)講解一下如何實(shí)現(xiàn) int 類型和字符串類型之間的互相轉(zhuǎn)換,需要的朋友可以參考下2023-09-09Golang自定義結(jié)構(gòu)體轉(zhuǎn)map的操作
這篇文章主要介紹了Golang自定義結(jié)構(gòu)體轉(zhuǎn)map的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12GO語言標(biāo)準(zhǔn)錯(cuò)誤處理機(jī)制error用法實(shí)例
這篇文章主要介紹了GO語言標(biāo)準(zhǔn)錯(cuò)誤處理機(jī)制error用法,實(shí)例分析了錯(cuò)誤處理機(jī)制的具體用法,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12Go語言fsnotify接口實(shí)現(xiàn)監(jiān)測(cè)文件修改
這篇文章主要為大家介紹了Go語言fsnotify接口實(shí)現(xiàn)監(jiān)測(cè)文件修改的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06go 迭代string數(shù)組操作 go for string[]
這篇文章主要介紹了go 迭代string數(shù)組操作 go for string[],具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12