如何使用Golang創(chuàng)建與讀取Excel文件
摘要
本文提出一種使用 Golang 進(jìn)行 Excel 文件創(chuàng)建和讀取的方案。首先對(duì)問(wèn)題進(jìn)行分析,引出方案的基本架構(gòu);然后分章節(jié)描述了 Excelize 基礎(chǔ)庫(kù)的基本用法,以及 Excel 數(shù)據(jù)在 Golang 中的表示和解析方式,并進(jìn)一步提出了應(yīng)對(duì)大規(guī)模數(shù)據(jù)寫入場(chǎng)景的優(yōu)化方法;最后,指出了一些可能遇到的問(wèn)題和對(duì)策。
引言
飛書是業(yè)界領(lǐng)先的下一代企業(yè)協(xié)作與管理平臺(tái),集合了很多細(xì)分領(lǐng)域的優(yōu)秀 ToB 產(chǎn)品。作者所在的部門,負(fù)責(zé)員工持股計(jì)劃(Employee Stock Ownership Plan,ESOP)相關(guān)系統(tǒng)的研發(fā),主要的后端開發(fā)語(yǔ)言為 Golang 。系統(tǒng)管理員,需要定期對(duì)公司 ESOP 的參與人信息,以及股權(quán)激勵(lì)的授予、歸屬、取消等信息,以Excel表格的形式進(jìn)行匯總,為相關(guān)決策提供參考和依據(jù);必要時(shí),也需要通過(guò)修改 Excel 數(shù)據(jù)表,上傳系統(tǒng),實(shí)現(xiàn)參與人、授予等信息的批量修改。
總而言之,隨著 Golang 的廣泛應(yīng)用,如何使用 Golang 進(jìn)行 Excel 數(shù)據(jù)表的讀取和創(chuàng)建,是一個(gè)值得探討的問(wèn)題。本文將描述一套完整的 Excel 文件創(chuàng)建和讀取的方案,方案力圖做到靈活通用,和具體的業(yè)務(wù)數(shù)據(jù)類型無(wú)關(guān),同時(shí)能夠兼顧大規(guī)模數(shù)據(jù)導(dǎo)出的效率。最后,分享了一些研發(fā)過(guò)程中遇到的問(wèn)題,希望能夠避免讀者再次踩坑。
正文
架構(gòu)
在開始具體深入地描述我們的方案之前,不妨先思考一下,實(shí)現(xiàn)這樣一個(gè)方案,需要解決的問(wèn)題都有哪些?數(shù)據(jù)的處理大致應(yīng)該是一個(gè)什么樣的流程?下圖是ESOP系統(tǒng)中,涉及到Excel文件創(chuàng)建和讀取的部分業(yè)務(wù)場(chǎng)景。
文件對(duì)象
很自然地,我們需要引入一個(gè) Excel 文件對(duì)象,此對(duì)象應(yīng)該包含一個(gè) Excel 工作簿的所有信息:有哪些工作表,每個(gè)工作表都有哪些列,每一行數(shù)據(jù)是什么,單元格和文本的格式是什么樣的,某一列是否包含枚舉值,等等。我們對(duì)文件的任何操作,無(wú)論是數(shù)據(jù)的增刪改,還是格式樣式的變更,亦或是文件的打開和保存,都應(yīng)該將這個(gè)文件對(duì)象作為切入點(diǎn)。
數(shù)據(jù)的表示
一個(gè) Excel 工作表,可以很自然地和 Golang 結(jié)構(gòu)體聯(lián)系起來(lái),工作表的每一列對(duì)應(yīng)結(jié)構(gòu)體中的一個(gè)字段。然而,只定義一個(gè)基本的結(jié)構(gòu)體還不夠,至少還應(yīng)該想辦法保存每個(gè)字段對(duì)應(yīng)的 Excel 列名、數(shù)據(jù)驗(yàn)證等等。
數(shù)據(jù)的解析
用 Golang 結(jié)構(gòu)體表示了一個(gè) Excel 工作表,自然還需要一種方法來(lái)解釋結(jié)構(gòu)體中記錄的各種 Excel 數(shù)據(jù)信息,這樣,程序代碼才知道如何將結(jié)構(gòu)體數(shù)據(jù)正確地寫入文件對(duì)象,以及反過(guò)來(lái),如何讀取文件對(duì)象中的數(shù)據(jù),來(lái)還原 Golang 結(jié)構(gòu)體。數(shù)據(jù)表示和解析的整體思路,如下圖所示。
實(shí)際架構(gòu)
幸運(yùn)的是,上面幾個(gè)問(wèn)題,我們都可以找到成熟且有力的工具來(lái)解決。方案基本的架構(gòu)如下圖所示。
文件對(duì)象的創(chuàng)建和各種操作,我們通過(guò) Excelize 基礎(chǔ)庫(kù)來(lái)實(shí)現(xiàn),后面會(huì)對(duì)該基礎(chǔ)庫(kù)進(jìn)行簡(jiǎn)要介紹。
Excel 數(shù)據(jù)的表示,我們使用包含 tag 的 Golang 結(jié)構(gòu)體實(shí)現(xiàn),數(shù)據(jù)值以外的其他信息,用某種格式記錄在 tag 中。
Excel 數(shù)據(jù)的解析,我們利用 Golang 的反射機(jī)制實(shí)現(xiàn)。通過(guò)反射,我們可以解析出結(jié)構(gòu)體每個(gè)字段的值以及 tag 中保存的其他有用信息。
Excelize 基礎(chǔ)庫(kù)
Excelize 是一個(gè)使用 Golang 編寫的,用于操作 Office Excel 文檔的基礎(chǔ)庫(kù),支持 Golang 1.15 及以上版本。下面對(duì)其基本用法進(jìn)行介紹,涉及到的各 API 的具體用法,可查看文章末尾給出的Excelize Doc鏈接。
文件
文件對(duì)象是本文大部分 Excel 文件操作的入口。使用 NewFile 函數(shù),可以創(chuàng)建一個(gè)空白的文件對(duì)象。如果需要用已有的 Excel 文件數(shù)據(jù)創(chuàng)建文件對(duì)象,可以使用 OpenReader 或者 OpenFile 函數(shù)。結(jié)束文件操作之后,通常需要將文件保存在本地,或者將文件輸出為字節(jié)數(shù)組,返回給前端供用戶下載,使用文件對(duì)象的 SaveAs 和 WriteToBuffer 方法,可以達(dá)到目的。
坐標(biāo)
在使用更多功能之前,必須搞清楚如何定位一個(gè)單元格或一個(gè)區(qū)域。
Excel 中使用形如“A1”這樣的坐標(biāo)來(lái)定位單元格。相應(yīng)的,在 Excelize 基礎(chǔ)庫(kù)中,可以通過(guò) CoordinatesToCellName 函數(shù),將行號(hào)和列號(hào)這個(gè)二元組,轉(zhuǎn)換為一個(gè)形如“A1”的坐標(biāo)。一些操作,需要通過(guò)兩個(gè)坐標(biāo)來(lái)確定生效區(qū)域,此時(shí),兩個(gè)相同的坐標(biāo)表示對(duì)一個(gè)單元格生效,兩個(gè)不同的坐標(biāo)表示對(duì)一個(gè)矩形區(qū)域生效,分別指向區(qū)域左上角和右下角的單元格。
樣式
樣式包含字體、文字大小、粗細(xì)、位置、顏色等屬性。Excelize 中,樣式可以通過(guò) NewStyle 方法創(chuàng)建,返回一個(gè)整數(shù)索引,唯一標(biāo)識(shí)這個(gè)樣式。通過(guò)文件對(duì)象的 SetCellStyle 方法,指定坐標(biāo)和樣式索引,可以為一個(gè)區(qū)域設(shè)置統(tǒng)一的樣式。
單元格操作
單元格的常用操作有設(shè)置高度和寬度、合并單元格、設(shè)置單元格數(shù)據(jù)等。
我們針對(duì)一行設(shè)置高度,針對(duì)一列設(shè)置寬度,分別使用文件對(duì)象中的 SetRowHeight 和 SetColWidth 方法進(jìn)行。
在“坐標(biāo)”部分,我們講解了如何確定一個(gè)區(qū)域,合并單元格的操作,就是一個(gè)例子,我們可以使用文件對(duì)象中的 MergeCell 方法來(lái)完成。
一般情況下,數(shù)據(jù)的寫入操作,在單元格層面進(jìn)行。使用文件對(duì)象中的 SetCellValue 方法,指定一個(gè)坐標(biāo),可以將 Golang 中常用的數(shù)據(jù)類型(包括無(wú)符號(hào)整數(shù)、有符號(hào)整數(shù)、浮點(diǎn)數(shù)、字節(jié)切片、字符串、時(shí)間、布爾類型等等)的值寫入對(duì)應(yīng)的單元格。
數(shù)據(jù)驗(yàn)證
數(shù)據(jù)驗(yàn)證功能,可以為某一列數(shù)據(jù)定義枚舉值,用戶可以使用下拉列表,為該列中某一行選擇要填入的值。
使用 NewDataValidation 函數(shù),可以創(chuàng)建一個(gè)數(shù)據(jù)驗(yàn)證對(duì)象,不妨命名為 vd 。為了指定 vd 的生效范圍,需要為 vd 設(shè)置一個(gè) Sqref 屬性,格式形如“A1:A10”,表示此 vd 對(duì)第 1 列中第 1 行到第 10 行的數(shù)據(jù)生效。然后,使用 vd 的 SetDropList 方法,設(shè)置下拉列表的內(nèi)容。最后,使用文件對(duì)象中的 AddDataValidation 方法,將此 vd 寫入文件。
數(shù)據(jù)的表示和解析
表示
根據(jù)“架構(gòu)”部分的設(shè)想,我們可以定義一個(gè)帶有 tag 的結(jié)構(gòu)體,來(lái)表示一個(gè) Excel 工作表。
Golang 結(jié)構(gòu)體的 tag ,是以鍵值對(duì)的形式表示的。為了和其他用途的 tag 進(jìn)行區(qū)分,我們將本方案的 tag 信息,用一個(gè)名為 ex 的鍵來(lái)表示,而 ex 的值,仍然沿用鍵值對(duì)的形式,如下列代碼所示:
type PeopleInfo struct { PeopleNo string `ex:"head:工號(hào);type:string;required;color:#0070C0"` PeopleName string `ex:"head:姓名;type:string;required"` BirthDate string `ex:"head:出生日期;type:date;omitempty"` EmploymentStatus string `ex:"head:在職狀態(tài);type:string;select:在職,離職"` }
我們可以為ex設(shè)計(jì)下列屬性:
- head,指定了此結(jié)構(gòu)體字段對(duì)應(yīng)的 Excel 列名。
- type,表示在使用反射進(jìn)行數(shù)據(jù)解析時(shí),會(huì)將此結(jié)構(gòu)體字段的值作為指定的類型處理。
- select,表示此字段所在的列,包含一個(gè)下拉列表,列表中的枚舉值由 select 后面的值指定。
- required,表示此字段必須包含非零值,否則在寫入 Excel 時(shí)會(huì)報(bào)錯(cuò)。
- omitempty,表示此字段如果是零值,則對(duì)應(yīng)的單元格留空。
- color,指定了列名所在單元格的顏色,通過(guò)這個(gè)字段,可以為不同的列名設(shè)置不同的底色,賦予一些含義,例如,可以將必填的列和選填的列,設(shè)置不同的底色??梢酝ㄟ^(guò) Excel 的 RGB 顏色設(shè)置窗口,查看不同顏色對(duì)應(yīng)的色號(hào),作為 color 屬性的值。
此外,我們還要定義一個(gè)結(jié)構(gòu)體,保存 ex 的解析結(jié)果,結(jié)構(gòu)體不妨命名為 Setting :
type Setting struct { Head string Type string Select []string Required bool OmitEmpty bool Color string }
解析
使用 Golang 的反射機(jī)制,對(duì)類似于 PeopleInfo 這樣的結(jié)構(gòu)體,我們可以抽取每個(gè)字段的ex值,進(jìn)行字符串處理后,組裝成Setting對(duì)象。示例代碼如下:
import reflect // 解析第idx個(gè)字段的ex func ParseEx(idx int, data interface{}) *Setting { tp := reflect.ValueOf(data).Type().Elem().Elem() // 獲得結(jié)構(gòu)體的反射Type field := tp.Field(idx) exStr := field.Tag.Get("ex") // 獲得tag中ex的值 setting := &Setting{} // 下面可通過(guò)對(duì)exStr字符串進(jìn)行切分,來(lái)組裝Setting對(duì)象,較為簡(jiǎn)單,省略 ... return setting } func main() { ParseEx(0, []*PeopleInfo{{}}) }
由于反射機(jī)制較為抽象,這里不再贅述,對(duì)反射不熟悉的讀者,可以查看文章末尾給出的 Golang reflect 鏈接。
組裝了 Setting 之后,我們可以繼續(xù)通過(guò)反射,來(lái)獲取結(jié)構(gòu)體中各字段的值,然后使用前面介紹過(guò)的一些 API ,將這些信息寫入 Excel 文件。
下面給出創(chuàng)建 Excel 文件的示例代碼,代碼對(duì) omitempty 和 type 屬性進(jìn)行了處理,并將部分?jǐn)?shù)據(jù)寫入文件對(duì)象。其他 ex 屬性的處理,因篇幅有限,不再演示,讀者有興趣可以自己嘗試實(shí)現(xiàn)。
import reflect import "github.com/xuri/excelize/v2" // 寫入第1行數(shù)據(jù)的第idx個(gè)字段 func WriteFirstRow(ef *excelize.File, idx int, data interface{}) error { firstRow := reflect.ValueOf(data).Index(0).Elem() // 第1個(gè)數(shù)據(jù)的反射Value v := firstRow.Field(idx) // 第idx個(gè)字段的反射Value setting := ParseEx(idx, data) // 第idx個(gè)字段解析出來(lái)的ex信息 // 處理omitempty if setting.OmitEmpty && v.IsZero() { return nil } var val interface{} // 處理type switch setting.Type { case "string": val = v.String() case ... } // Excel列號(hào)從1開始,所以列號(hào)是idx+1;行號(hào)從2開始,因?yàn)榈?行要顯示列名 axis, err := excelize.CoordinatesToCellName(idx+1, 2) if err != nil { return err } // 將數(shù)據(jù)寫入默認(rèn)工作表Sheet1中axis坐標(biāo)處的單元格 return ef.SetCellValue("Sheet1", axis, val) } func main() { ef := excelize.NewFile() WriteFirstRow(ef, 0, []*PeopleInfo{{PeopleNo: "test"}}) ef.SaveAs("people_info.xlsx") }
上面給出的是創(chuàng)建 Excel 文件的示例。讀取 Excel 文件的過(guò)程是類似的,首先從二進(jìn)制數(shù)據(jù)創(chuàng)建出文件對(duì)象,然后根據(jù)文件對(duì)象中的每一列數(shù)據(jù),生成對(duì)應(yīng)的結(jié)構(gòu)體對(duì)象。示例代碼如下。
import reflect import "github.com/xuri/excelize/v2" // 讀取第1行數(shù)據(jù)的第idx列,假定idx從0開始,只有一個(gè)默認(rèn)工作表Sheet1,數(shù)據(jù)從第2行開始 func ReadFirstRow(ef *excelize.File, idx int, holder interface{}) error { rows, err := ef.GetRows("Sheet1") // 所有行 if err != nil { return err } row := rows[1] tp := reflect.TypeOf(holder).Elem().Elem().Elem() // 結(jié)構(gòu)體的類型 val := reflect.New(tp) // 創(chuàng)建一個(gè)新的結(jié)構(gòu)體對(duì)象 field := val.Elem().Field(idx) // 第idx個(gè)字段的反射Value cellValue := row[idx] // 第idx個(gè)字段對(duì)應(yīng)的Excel數(shù)據(jù) field.SetString(cellValue) // 將Excel數(shù)據(jù)保存到結(jié)構(gòu)體對(duì)象的對(duì)應(yīng)字段中 listV := reflect.ValueOf(holder) listV.Elem().Set(reflect.Append(listV.Elem(), val)) // 將結(jié)構(gòu)體對(duì)象添加到holder中 return nil } func main() { ef, _ := excelize.OpenFile("people_info.xlsx") holder := make([]*PeopleInfo, 0, 10) ReadFirstRow(ef, 0, &holder) }
本節(jié)描述了如何使用 Golang 來(lái)表示和解析 Excel 數(shù)據(jù),以及在此基礎(chǔ)上如何創(chuàng)建和讀取 Excel 文件。示例代碼中對(duì) Excel 文件的寫入和讀取操作函數(shù),使用 interface 類型的參數(shù)作為數(shù)據(jù)提供方或接收方,和具體的業(yè)務(wù)數(shù)據(jù)類型無(wú)關(guān),因此該方案具備通用性。
大規(guī)模數(shù)據(jù)的寫入
之前演示的 Excel 文件寫入方式,是在單元格層面進(jìn)行的,在大規(guī)模數(shù)據(jù)寫入的場(chǎng)景下,耗時(shí)長(zhǎng),體驗(yàn)差。Excelize 提供了一套流式寫入 API,以行為單位寫入 Excel 數(shù)據(jù),能夠顯著提高大規(guī)模數(shù)據(jù)的寫入效率。
使用流式 API 寫入 Excel 數(shù)據(jù),首先需要使用文件對(duì)象的 NewStreamWriter 方法,創(chuàng)建一個(gè)流式寫入器。寫入一行數(shù)據(jù)時(shí),需要構(gòu)造一個(gè)切片,表示這一行數(shù)據(jù),切片中每個(gè)元素表示一個(gè)單元格信息,包含單元格的值和樣式。單元格元素,可以使用 Excelize 中提供的 Cell 數(shù)據(jù)類型來(lái)表示。之后,就可以通過(guò)流式寫入器的 SetRow 方法,將行數(shù)據(jù)寫入 Excel 文件。行的高度,可以在寫入時(shí)指定。示例代碼如下:
import reflect import "github.com/xuri/excelize/v2" // 寫入第1行數(shù)據(jù) func StreamWriteFirstRow(sw *excelize.StreamWriter, data interface{}) error { firstRow := reflect.ValueOf(data).Index(0).Elem() // 第1個(gè)數(shù)據(jù)的反射Value v := firstRow.Field(0) // 首個(gè)字段的反射Value style := &excelize.Style{ Alignment: &excelize.Alignment{ Horizontal: "left", Vertical: "center", }, } styleID, err := sw.File.NewStyle(style) // 創(chuàng)建樣式 if err != nil { return err } length := firstRow.NumField() // 結(jié)構(gòu)體字段數(shù)量 row := make([]interface{}, length) // 創(chuàng)建一個(gè)切片,表示一行數(shù)據(jù) row[0] = excelize.Cell{ Value: v.String(), StyleID: styleID, } // 這里只寫入首個(gè)字段 // 每一行都是從列號(hào)1開始;行號(hào)從2開始,因?yàn)榧俣ǖ?行要顯示列名 axis, err := excelize.CoordinatesToCellName(1, 2) if err != nil { return err } // 流式寫入行,并指定高度 return sw.SetRow(axis, row, excelize.RowOpts{Height: 16}) } func main() { ef := excelize.NewFile() sw, _ := ef.NewStreamWriter("Sheet1") StreamWriteFirstRow(sw, []*PeopleInfo{{PeopleNo: "test"}}) sw.Flush() ef.SaveAs("stream_people_info.xlsx") }
需要關(guān)注的問(wèn)題
大量枚舉值的設(shè)置
在“數(shù)據(jù)驗(yàn)證”部分,我們提到使用 SetDropList 方法可以設(shè)置下拉列表。然而,這樣設(shè)置的下拉列表是有局限性的:每個(gè)枚舉值使用逗號(hào)拼接后得到的字符串,其總長(zhǎng)度不得超過(guò) 255 個(gè)字符。
如果超過(guò)了這個(gè)限制,我們需要?jiǎng)?chuàng)建一個(gè)工作表(假設(shè)名稱為 enum ),將枚舉值存儲(chǔ)在 enum 工作表的某一列中(假設(shè)存儲(chǔ)在 A 列,第 2 行到第 10 行),然后通過(guò) vd 的 SetSqrefDropList 方法設(shè)置下拉列表,此方法通過(guò)一個(gè)形如“enum!A2:A10”的字符串來(lái)指定枚舉值的存儲(chǔ)位置,即 enum 工作表 A 列的第 2 行到第 10 行。
大工作表的讀取
讀取 Excel 文件時(shí),我們會(huì)基于已有的物理文件來(lái)創(chuàng)建文件對(duì)象,如果其中有一個(gè)很大的工作表,那么當(dāng)我們將這個(gè)文件對(duì)象另存為一個(gè)新的物理文件時(shí),可能會(huì)發(fā)現(xiàn)文件變小了,且無(wú)法正常打開。
Excelize 庫(kù)通過(guò)一些參數(shù),來(lái)限制打開和讀取工作薄時(shí)的內(nèi)存使用。其中,WorksheetUnzipMemLimit 參數(shù)限制了 unzip 一個(gè)工作表時(shí)允許使用的最大內(nèi)存,默認(rèn)為 16 MB 。當(dāng)一個(gè)工作表大小超過(guò)這一默認(rèn)值時(shí),此工作表的數(shù)據(jù)會(huì) unzip 到操作系統(tǒng)的臨時(shí)文件中。然而,當(dāng)我們進(jìn)行另存為操作時(shí),這些臨時(shí)文件的數(shù)據(jù)卻被 Excelize 的相關(guān)函數(shù)忽略了。
這可能是 Excelize 庫(kù)的一個(gè) bug ,但是我們可以通過(guò)增大 WorksheetUnzipMemLimit 參數(shù)來(lái)規(guī)避。這一參數(shù)的值,可根據(jù)具體業(yè)務(wù)場(chǎng)景來(lái)設(shè)置,最大可以設(shè)置為和 UnzipSizeLimit 參數(shù)相同,后者是打開整個(gè)工作簿時(shí)總的內(nèi)存使用限制,默認(rèn)為 16 GB 。
流式寫入的注意事項(xiàng)
流式操作有自己的一套 API ,用于數(shù)據(jù)寫入、合并單元格、設(shè)置列寬等操作。流式 API 不能和普通的非流式 API 混用,否則可能無(wú)法正確寫入數(shù)據(jù)或設(shè)置格式。使用流式 API 設(shè)置列寬,需要在寫入數(shù)據(jù)之前進(jìn)行。流式寫入完成之后,需要調(diào)用流式寫入器的 Flush 方法來(lái)結(jié)束寫入,否則保存文件時(shí)可能會(huì)丟失數(shù)據(jù)。
結(jié)語(yǔ)
本文對(duì) Golang 中創(chuàng)建和讀取 Excel 文件所涉及的各方面問(wèn)題,進(jìn)行了總結(jié)歸納,并提出了一套完整的方案。此方案使用 Golang 結(jié)構(gòu)體的 tag ,以及 Golang 反射機(jī)制,對(duì) Excel 數(shù)據(jù)進(jìn)行定義和解釋,實(shí)現(xiàn)了 Golang 結(jié)構(gòu)體和 Excel 數(shù)據(jù)的雙向映射,同時(shí)使用成熟強(qiáng)大的 Excelize 基礎(chǔ)庫(kù),對(duì) Excel 文件進(jìn)行創(chuàng)建、寫入、讀取等操作。
希望讀者能有所收獲,為解決實(shí)際的問(wèn)題提供思路,也歡迎大家對(duì)方案中的不足之處提出改進(jìn)意見。
參考資料
- Excelize Doc:xuri.me/excelize/zh…
- Golang reflect:pkg.go.dev/reflect
總結(jié)
到此這篇關(guān)于如何使用Golang創(chuàng)建與讀取Excel文件的文章就介紹到這了,更多相關(guān)Golang創(chuàng)建讀取Excel內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go接口構(gòu)建可擴(kuò)展Go應(yīng)用示例詳解
本文深入探討了Go語(yǔ)言中接口的概念和實(shí)際應(yīng)用場(chǎng)景。從基礎(chǔ)知識(shí)如接口的定義和實(shí)現(xiàn),到更復(fù)雜的實(shí)戰(zhàn)應(yīng)用如解耦與抽象、多態(tài)、錯(cuò)誤處理、插件架構(gòu)以及資源管理,文章通過(guò)豐富的代碼示例和詳細(xì)的解釋,展示了Go接口在軟件開發(fā)中的強(qiáng)大功能和靈活性2023-10-10Golang中的[]byte與16進(jìn)制(String)之間的轉(zhuǎn)換方式
這篇文章主要介紹了Golang中的[]byte與16進(jìn)制(String)之間的轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Go語(yǔ)言{}大括號(hào)的特殊用法實(shí)例探究
這篇文章主要為大家介紹了Go語(yǔ)言{}大括號(hào)的特殊用法實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01golang如何用type-switch判斷interface變量的實(shí)際存儲(chǔ)類型
這篇文章主要介紹了golang如何用type-switch判斷interface變量的實(shí)際存儲(chǔ)類型,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04golang基于websocket通信tcp keepalive研究記錄
這篇文章主要為大家介紹了golang基于websocket通信tcp keepalive研究記錄,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Go到底能不能實(shí)現(xiàn)安全的雙檢鎖(推薦)
這篇文章主要介紹了Go到底能不能實(shí)現(xiàn)安全的雙檢鎖,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05詳解Go語(yǔ)言中init的使用與常見應(yīng)用場(chǎng)景
Go?中有一個(gè)特別的?init()?函數(shù),它主要用于包的初始化,這篇文章將以此為主題介紹?Go?中?init()?函數(shù)的使用和常見使用場(chǎng)景,希望對(duì)大家有所幫助2024-02-02