Go?CSV包實(shí)現(xiàn)結(jié)構(gòu)體和csv內(nèi)容互轉(zhuǎn)工具詳解
引言
大家在開(kāi)發(fā)中一定遇到過(guò)將數(shù)據(jù)導(dǎo)出成csv格式文件的需求。go標(biāo)準(zhǔn)庫(kù)中的csv包是只能寫(xiě)入字符串類(lèi)型的切片。而在go中一般都是將內(nèi)容寫(xiě)入到結(jié)構(gòu)體中。所以,若使用標(biāo)準(zhǔn)的csv包,就需要將結(jié)構(gòu)體先轉(zhuǎn)換成對(duì)應(yīng)的字符串類(lèi)型,再寫(xiě)入文件。那可不可以將結(jié)構(gòu)體對(duì)象直接輸出成csv格式內(nèi)容呢?
今天給大家推薦的就是一個(gè)能將結(jié)構(gòu)體和csv內(nèi)容進(jìn)行快速互轉(zhuǎn)的工具包:gocsv
gocsv小檔案
| gocsv 小檔案 | |||
|---|---|---|---|
| star | 1.5 k | used by | 1.6k |
| contributors | 80 | 作者 | gocarina |
| 功能簡(jiǎn)介 | 提供一個(gè)簡(jiǎn)單、高效地將csv內(nèi)容和結(jié)構(gòu)體進(jìn)行互轉(zhuǎn)的功能 | ||
| 項(xiàng)目地址 | github.com/gocarina/go… | ||
| 相關(guān)知識(shí) | reflect、結(jié)構(gòu)體tag |
gocsv的基本功能
gocsv包的最基本的作用就是能夠方便的將csv內(nèi)容轉(zhuǎn)換到對(duì)應(yīng)的結(jié)構(gòu)體上,或者將結(jié)構(gòu)體的內(nèi)容快速的轉(zhuǎn)換成csv格式(包括寫(xiě)入文件)。

gocsv.UnmarshalFile函數(shù):csv內(nèi)容轉(zhuǎn)成結(jié)構(gòu)體
假設(shè)文件中的內(nèi)容如下:
client_id,client_name,client_age 1,Jose,42 2,Daniel,26 3,Vincent,32
然后從文件中讀取出內(nèi)容,并直接轉(zhuǎn)換到結(jié)構(gòu)體Client上,如下:
package main
import (
"fmt"
"os"
"github.com/gocarina/gocsv"
)
type NotUsed struct {
Name string
}
type Client struct { // Our example struct, you can use "-" to ignore a field
Id string `csv:"client_id"`
Name string `csv:"client_name"`
Age string `csv:"client_age"`
NotUsedString string `csv:"-"`
NotUsedStruct NotUsed `csv:"-"`
}
func main() {
clientsFile, err := os.OpenFile("clients.csv", os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
panic(err)
}
defer clientsFile.Close()
clients := []*Client{}
if err := gocsv.UnmarshalFile(clientsFile, &clients); err != nil { // Load clients from file
panic(err)
}
for _, client := range clients {
fmt.Println("Hello", client.Name)
}
}
gocsv.MarshalFile函數(shù):結(jié)構(gòu)體轉(zhuǎn)成csv文件
package main
import (
"fmt"
"os"
"github.com/gocarina/gocsv"
)
type NotUsed struct {
Name string
}
type Client struct { // Our example struct, you can use "-" to ignore a field
Id string `csv:"client_id"`
Name string `csv:"client_name"`
Age string `csv:"client_age"`
NotUsedString string `csv:"-"`
NotUsedStruct NotUsed `csv:"-"`
}
func main() {
clientsFile, err := os.OpenFile("clients.csv", os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
panic(err)
}
defer clientsFile.Close()
clients := []*Client{}
clients = append(clients, &Client{Id: "12", Name: "John", Age: "21"}) // Add clients
clients = append(clients, &Client{Id: "13", Name: "Fred"})
clients = append(clients, &Client{Id: "14", Name: "James", Age: "32"})
clients = append(clients, &Client{Id: "15", Name: "Danny"})
err = gocsv.MarshalFile(&clients, clientsFile) // Use this to save the CSV back to the file
if err != nil {
panic(err)
}
}
自定義類(lèi)型轉(zhuǎn)換器
gocsv包還可以給自定義的結(jié)構(gòu)體類(lèi)型定義csv和結(jié)構(gòu)體的互轉(zhuǎn)函數(shù)。只要自定義的類(lèi)型實(shí)現(xiàn)如下接口即可:
type TypeMarshaller interface {
MarshalCSV() (string, error)
}
// TypeUnmarshaller is implemented by any value that has an UnmarshalCSV method
// This converter is used to convert a string to your value representation of that string
type TypeUnmarshaller interface {
UnmarshalCSV(string) error
}
或者將結(jié)構(gòu)體轉(zhuǎn)換成csv字符串時(shí),需要實(shí)現(xiàn)如下接口:
// MarshalText encodes the receiver into UTF-8-encoded text and returns the result.
type TextMarshaler interface {
MarshalText() (text []byte, err error)
}
type TextUnmarshaler interface {
UnmarshalText(text []byte) error
}
例如,我們定義了一個(gè)結(jié)構(gòu)體DateTime,里面有一個(gè)time.Time類(lèi)型的屬性。并且DateTime類(lèi)型實(shí)現(xiàn)了TypeMarshaller接口的MarshalCSV函數(shù)和TypeUnmarshaller接口的UnmarshalCSV函數(shù)。如下:
type DateTime struct {
time.Time
}
// Convert the internal date as CSV string
func (date *DateTime) MarshalCSV() (string, error) {
return date.Time.Format("20060201"), nil
}
// You could also use the standard Stringer interface
func (date *DateTime) String() (string) {
return date.String() // Redundant, just for example
}
// Convert the CSV string as internal date
func (date *DateTime) UnmarshalCSV(csv string) (err error) {
date.Time, err = time.Parse("20060201", csv)
return err
}
type Client struct { // Our example struct with a custom type (DateTime)
Id string `csv:"id"`
Name string `csv:"name"`
Employed DateTime `csv:"employed"`
}
func main() {
client := []Client{
{
Id: "001",
Name: "Go學(xué)堂",
Employed: DateTime{time.Now()},
},
}
csvContent, _ := gocsv.MarshalString(client)
fmt.Println("csv:", csvContent) //輸出內(nèi)容是 001,Go學(xué)堂,20231003
}
當(dāng)我們運(yùn)行上述代碼,最終的輸出內(nèi)容是:
001,Go學(xué)堂,20231003
最后的日期就是按DateTime的MarshalCSV函數(shù)格式輸出的。
自定義CSV的Reader/Writer
在開(kāi)頭處我們提到,csv文件中的分隔符默認(rèn)是逗號(hào)。但也可以是其他字符。這就要求我們?cè)谧x取或?qū)懭胫爸付ê脙?nèi)容的分隔號(hào)。那么就可以通過(guò)自定義的Reader/Writer來(lái)覆蓋默認(rèn)的Reader/Writer的選項(xiàng)。如下:
- 指定讀取內(nèi)容的分割符是 "|"
gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
r := csv.NewReader(in)
r.Comma = '|'
return r // Allows use pipe as delimiter
})
- 指定寫(xiě)入的內(nèi)容是用 分割符 "|" 進(jìn)行分割的
gocsv.SetCSVWriter(func(out io.Writer) *gocsv.SafeCSVWriter {
writer := csv.NewWriter(out)
writer.Comma = '|'
return gocsv.NewSafeCSVWriter(writer)
})
gocsv包的特點(diǎn)總結(jié)
1、結(jié)構(gòu)體切片和csv內(nèi)容互轉(zhuǎn)。能夠?qū)⒔Y(jié)構(gòu)體切片(或數(shù)組)直接輸出成csv內(nèi)容或輸出到文件。反之亦然。
2、csv標(biāo)簽。其轉(zhuǎn)換過(guò)程是通過(guò)結(jié)構(gòu)體上的“csv”標(biāo)簽進(jìn)行關(guān)聯(lián)的。
3、csv標(biāo)簽對(duì)應(yīng)csv內(nèi)容表頭。當(dāng)結(jié)構(gòu)體和csv格式互轉(zhuǎn)時(shí),結(jié)構(gòu)體中的csv標(biāo)簽對(duì)應(yīng)的就是csv表格的表頭,結(jié)構(gòu)體中的字段順序?qū)?yīng)的就是csv文件列的順序。
4、底層依然是使用標(biāo)準(zhǔn)庫(kù)中的csv。在寫(xiě)入csv文件時(shí),底層實(shí)際上用的還是go標(biāo)準(zhǔn)庫(kù)中的encoding/csv/Writer結(jié)構(gòu)體的Write(row []string)方法。
5、自動(dòng)將結(jié)構(gòu)體字段的類(lèi)型轉(zhuǎn)換成字符串:大家看到標(biāo)準(zhǔn)csv包中的Write方法的入?yún)⑹莝tring類(lèi)型的切片,而在要轉(zhuǎn)換的結(jié)構(gòu)體上的字段可以是各種類(lèi)型。這里就是gocsv包中的一個(gè)特點(diǎn):可以將字段中的非string類(lèi)型轉(zhuǎn)換成string類(lèi)型,最終寫(xiě)入到csv文件中。
6、可自定義類(lèi)型轉(zhuǎn)換器。可以通過(guò)實(shí)現(xiàn)TypeMarshaller接口或TypeUnMarshaller接口對(duì)自定義類(lèi)型的內(nèi)容按對(duì)應(yīng)的格式輸出成csv內(nèi)容。
7、可自定義CSV的Reader/Writer來(lái)覆蓋默認(rèn)參數(shù)。比如csv格式的內(nèi)容默認(rèn)使用逗號(hào)分隔內(nèi)容。通過(guò)該功能我們可以指定使用其他分隔符的csv內(nèi)容。比如使用"|"或";"等。
這里需要注意的是 將csv文件的內(nèi)容一定是解析到結(jié)構(gòu)體類(lèi)型的切片或數(shù)組中。同樣,也只有是結(jié)構(gòu)體類(lèi)型的切片或數(shù)組才能直接寫(xiě)入到csv文件中。
以上,就是今天我們要分享的工具包,更多關(guān)于Go CSV包工具的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Golang?sync.Once實(shí)現(xiàn)單例模式的方法詳解
Go?語(yǔ)言的?sync?包提供了一系列同步原語(yǔ),其中?sync.Once?就是其中之一。本文將深入探討?sync.Once?的實(shí)現(xiàn)原理和使用方法,幫助大家更好地理解和應(yīng)用?sync.Once,需要的可以參考一下2023-05-05
Go語(yǔ)言類(lèi)型內(nèi)嵌和結(jié)構(gòu)體內(nèi)嵌的具體使用
本文主要介紹了Go語(yǔ)言類(lèi)型內(nèi)嵌和結(jié)構(gòu)體內(nèi)嵌的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
基于Go語(yǔ)言簡(jiǎn)單實(shí)現(xiàn)事件管理器
在編程中,事件管理器是一種常見(jiàn)的工具,用于通過(guò)通知來(lái)觸發(fā)操作,本文將介紹一個(gè)簡(jiǎn)單的Go事件管理器的實(shí)現(xiàn),并通過(guò)異步改進(jìn)提高其性能,感興趣的可以了解下2023-11-11
Golang使用反射的動(dòng)態(tài)方法調(diào)用詳解
Go是一種靜態(tài)類(lèi)型的語(yǔ)言,提供了大量的安全性和性能。這篇文章主要和大家介紹一下Golang使用反射的動(dòng)態(tài)方法調(diào)用,感興趣的小伙伴可以了解一下2023-03-03

