Go CSV包实现结构体和csv内容互转工具详解

目录
  • 引言
  • gocsv小档案
  • gocsv的基本功能
  • gocsv.UnmarshalFile函数:csv内容转成结构体
  • gocsv.MarshalFile函数:结构体转成csv文件
  • 自定义类型转换器
  • 自定义CSV的Reader/Writer
  • gocsv包的特点总结

引言

大家在开发中一定遇到过将数据导出成csv格式文件的需求。go标准库中的csv包是只能写入字符串类型的切片。而在go中一般都是将内容写入到结构体中。所以,若使用标准的csv包,就需要将结构体先转换成对应的字符串类型,再写入文件。那可不可以将结构体对象直接输出成csv格式内容呢?

今天给大家推荐的就是一个能将结构体和csv内容进行快速互转的工具包:gocsv

gocsv小档案

gocsv 小档案
star 1.5 k used by 1.6k
contributors 80 作者 gocarina
功能简介 提供一个简单、高效地将csv内容和结构体进行互转的功能
项目地址 github.com/gocarina/go…
相关知识 reflect、结构体tag

gocsv的基本功能

gocsv包的最基本的作用就是能够方便的将csv内容转换到对应的结构体上,或者将结构体的内容快速的转换成csv格式(包括写入文件)。

gocsv.UnmarshalFile函数:csv内容转成结构体

假设文件中的内容如下:

client_id,client_name,client_age
1,Jose,42
2,Daniel,26
3,Vincent,32

然后从文件中读取出内容,并直接转换到结构体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函数:结构体转成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)
	}
}

自定义类型转换器

gocsv包还可以给自定义的结构体类型定义csv和结构体的互转函数。只要自定义的类型实现如下接口即可:

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
}

或者将结构体转换成csv字符串时,需要实现如下接口:

// 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
}

例如,我们定义了一个结构体DateTime,里面有一个time.Time类型的属性。并且DateTime类型实现了TypeMarshaller接口的MarshalCSV函数和TypeUnmarshaller接口的UnmarshalCSV函数。如下:

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学堂",
			Employed: DateTime{time.Now()},
		},
	}
	csvContent, _ := gocsv.MarshalString(client)
	fmt.Println("csv:", csvContent) //输出内容是 001,Go学堂,20231003
}

当我们运行上述代码,最终的输出内容是:

001,Go学堂,20231003

最后的日期就是按DateTime的MarshalCSV函数格式输出的。

自定义CSV的Reader/Writer

在开头处我们提到,csv文件中的分隔符默认是逗号。但也可以是其他字符。这就要求我们在读取或写入之前指定好内容的分隔号。那么就可以通过自定义的Reader/Writer来覆盖默认的Reader/Writer的选项。如下:

  • 指定读取内容的分割符是 "|"
gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
    r := csv.NewReader(in)
	r.Comma = '|'
    return r // Allows use pipe as delimiter
})
  • 指定写入的内容是用 分割符 "|" 进行分割的
gocsv.SetCSVWriter(func(out io.Writer) *gocsv.SafeCSVWriter {
	writer := csv.NewWriter(out)
    writer.Comma = '|'
    return gocsv.NewSafeCSVWriter(writer)
})

gocsv包的特点总结

1、结构体切片和csv内容互转。能够将结构体切片(或数组)直接输出成csv内容或输出到文件。反之亦然。

2、csv标签。其转换过程是通过结构体上的“csv”标签进行关联的。

3、csv标签对应csv内容表头。当结构体和csv格式互转时,结构体中的csv标签对应的就是csv表格的表头,结构体中的字段顺序对应的就是csv文件列的顺序。

4、底层依然是使用标准库中的csv。在写入csv文件时,底层实际上用的还是go标准库中的encoding/csv/Writer结构体的Write(row []string)方法。

5、自动将结构体字段的类型转换成字符串:大家看到标准csv包中的Write方法的入参是string类型的切片,而在要转换的结构体上的字段可以是各种类型。这里就是gocsv包中的一个特点:可以将字段中的非string类型转换成string类型,最终写入到csv文件中。

6、可自定义类型转换器。可以通过实现TypeMarshaller接口或TypeUnMarshaller接口对自定义类型的内容按对应的格式输出成csv内容。

7、可自定义CSV的Reader/Writer来覆盖默认参数。比如csv格式的内容默认使用逗号分隔内容。通过该功能我们可以指定使用其他分隔符的csv内容。比如使用"|"或";"等。

这里需要注意的是 将csv文件的内容一定是解析到结构体类型的切片或数组中。同样,也只有是结构体类型的切片或数组才能直接写入到csv文件中。

以上,就是今天我们要分享的工具包,更多关于Go CSV包工具的资料请关注我们其它相关文章!

(0)

相关推荐

  • Go实现简单的数据库表转结构体详解

    目录 前言 设计过程 迭代升级 前言 码上源码 package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" "strings" ) /** 自动将表生成 model结构, 通过创建数据库连接,读取数据库的所有表并对所有的表元数据封装转化实体结构体 */ type SchemaMeta struct { Field string Ty

  • go语言结构体指针操作示例详解

    目录 指针 go指针操作 不能操作不合法指向 new函数 指针做函数的参数 数组指针 结构体指针变量 结构体成员普通变量 结构体成员指针变量 结构体比较和赋值 结构体作为函数参数 指针 指针是代表某个内存地址的值.内存地址储存另一个变量的值. 指针(地址),一旦定义了不可改变,指针指向的值可以改变 go指针操作 1.默认值nil,没有NULL常量 2.操作符“&”取变量地址,“*“通过指针(地址)访问目标对象(指向值) 3.不支持指针运算,不支持“->”(箭头)运算符,直接用“.”访问目标成

  • c++ STL之list对结构体的增加,删除,排序等操作详解

    对STL中的list进一步学习,编程过程中对结构体的操作很多. 全部代码如下: /* Project:list对结构体的使用 Date: 2018/07/14 Author: Frank Yu 常用函数:int size() 返回容器元素个数:bool empty() 判断容器是否为空,true为空: 增加函数:void push_back(元素) 尾元素后增加一个元素:push_front(元素) 首元素前增加一个元素: iterator insert(lit,元素)在迭代器指针lit前插入元

  • C语言结构体成员赋值的深拷贝与浅拷贝详解

    目录 浅拷贝 结构体中不存在指针成员变量时 结构体中存在指针成员变量时 深拷贝 结论 浅拷贝 C语言中的浅拷贝是指在拷贝过程中,对于指针型成员变量只拷贝指针本身,而不拷贝指针所指向的目标,它按字节复制的.我们分几种情况举例子来看一下. 结构体中不存在指针成员变量时 代码如下: #include <stdio.h> typedef struct { char name[64]; int age; }Member; int main(){ Member stu1 = { "LiMing&

  • C++中结构体和Json字符串互转的问题详解

    大家有没有在项目中遇到过,将一些预定义的本地结构体转换为Json字符串后,发送到网络中的情形.那我猜想下大家常规的做法:写一个函数,传入结构体的指针,然后在函数中对结构体的每一个成员根据其类型,使用Json类库的赋值方法,直接或间接创建Json子对象,组成一个内存树状结构,最后调用Json类库的方法生成字符串.这样的做法似乎比较完美,工作完成得很好,确实也挑不出什么毛病来,让我们先看看在golang中是怎么做的: type Person struct { Name string Age int

  • Python包中__init__.py文件的作用与用法实例详解

    目录 前言 一.__init__.py文件简介 二.__init__.py文件的使用案例 2.1.没有__init__.py文件的目录,使用import xx.xx会报错吗? 2.2.在__init__.py文件中定义变量或者逻辑将在导入时候被执行 2.3.在__init__.py文件中定义变量或者方法,可以被直接导入使用 2.4.在__init__.py文件中使用__all__方法 总结 前言 在Python工程中,我们经常可以看到带有“__init__.py”文件的目录,在PyCharm中,

  • MySQL表结构变更你不可不知的Metadata Lock详解

    前言 想必玩过mysql的人对Waiting for table metadata lock肯定不会陌生,一般都是进行alter操作时被堵住了,导致了我们在show processlist 时,看到线程的状态是在等metadata lock.本文会对MySQL表结构变更的Metadata Lock进行详细的介绍. 在线上进行DDL操作时,相对于其可能带来的系统负载,其实,我们最担心的还是MDL其可能导致的阻塞问题. 一旦DDL操作因获取不到MDL被阻塞,后续其它针对该表的其它操作都会被阻塞.典型

  • PHP+fiddler抓包采集微信文章阅读数点赞数的思路详解

    简介: 分析接口知道要获取文章阅读数和点赞数必须有key和uin这两个关键参数,不同公众号key不一样(据说有万能微信key,不懂怎么搞到),同一个公众号key大概半小时会过期 提交链接获取文章阅读量api 思路: 1.将客户端请求阅读量接口的请求拦截转发到自己服务器,这样就可以获取到key ,用__biz关联缓存半小时 2.提交文章链接进行查询时,服务器从文章链接里获取__biz,查询是否缓存了当前公众号对应的key,有的话进行第3步,没有进行第4步. 3.curl请求https://mp.w

  • java实现图的邻接表存储结构的两种方式及实例应用详解

    前言 本篇来谈一谈图的邻接表实现的两种方式,首先我们明确一点"学会图的邻接表实现的关键点在于":你所建立的图的邻接表的对象是什么! 首先我们看一下<算法导论>中关于图的邻接表的定义: 图G=(V,E)的邻接表表示有一个包含 |V| 个列表的数组Adj所组成,其中每个列表对应于V中的一个顶点,对于每一个u∈V,邻接表Adj[u]包含所有满足条件(u,v)∈E的顶点v,亦即,Adj[u]包含图G中所有和顶点u相邻的顶点.(或者他也可能指向这些顶点的指针),每个邻接表中的顶点一般

  • golang 如何用反射reflect操作结构体

    背景 需要遍历结构体的所有field 对于exported的field, 动态set这个field的value 对于unexported的field, 通过强行取址的方法来获取该值(tricky?) 思路 下面的代码实现了从一个strct ptr对一个包外结构体进行取值的操作,这种场合在笔者需要用到反射的场合中出现比较多 simpleStrtuctField 函数接受一个结构体指针,因为最后希望改变其值,所以传参必须是指针.然后解引用. 接下来遍历结构体的每个field, exported字段是

  • Go 结构体序列化的实现

    目录 更改JSON对象中的键 在JSON对象中隐藏结构体字段 附加内容 结构体标签string指令 本文,我们将回到之前写的showMovieHandler方法,并更新它以返回一个JSON响应,表示系统中的单个电影信息.类似于: {     "id": 123,     "title": "Casablanca",      "runtime": 102,      "genres": [        

  • .NET使用结构体替代类提升性能优化的技巧

    目录 前言 现实的案例 内存占用 计算速度 总结 附录 前言 我们知道在C#和Java明显的一个区别就是C#可以自定义值类型,也就是今天的主角struct,我们有了更加方便的class为什么微软还加入了struct呢?这其实就是今天要谈到的一个优化性能的Tips使用结构体替代类.那么使用结构体替代类有什么好处呢?在什么样的场景需要使用结构体来替代类呢?今天的文章为大家一一解答.注意:本文全部都以x64位平台为例 现实的案例 举一个现实系统的例子,大家都知道机票购票的流程,开始选择起抵城市和机场(

随机推荐