Go语言处理csv文件数据

Go自带的库文件encoding/csv 可以用来解析 csv 文件,本例子将演示通过标准库文件以及数据帧两种方式解析csv文件

一、通过标准库文件encoding/csv

1、首先我们制作一个csv文件,此文件字段之间通过逗号分隔,如图:

2、定义go文件,打开csv文件并读取,如图:

  • 通过Open()打开文件,返回os库的File结构体,此结构体实现了io.Reader接口,注意Open()需要关闭
  • 通过csv库的NewReader()读取打开后的文件,返回csv库的Reader结构体指针,NewReader参数为io.Reader接口,File也实现了此接口,因此 f 可以直接作为参数传递
  • 通过for无限循环以及Reader结构体的Read()方法读取内容,返回字符串切片
  • io.EOF:数据读取结束后会返回EOF,因此判断err等于io.EOF时此时表示数据已经读取完毕
  • 最后将读取的数据添加到定义的二维切片中

除了上面Read()方法还可以通过ReadAll()方法直接返回二维切片,如图:

最终打印结果如图:

注意:如果csv文件不是以逗号分隔或者文件中包含注释的行,此时可以使用csv.Reader.Comma和csv.Reader.Comment来处理

向csv中写入文件

1、首先查看csv文件内容,注意在将此csv文件上传到Linux之前确认好编码为utf-8,如图:

2、获取每行的日期数据的年份差值,放在行后,并输出到新文件中,如下:

package main

import (
	"encoding/csv"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
)

func main() {
	f, err := os.Open("./test.csv")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	rd := csv.NewReader(f)
	var res [][]string
	for {
		rs, err := rd.Read() //读取结果为切片
		if err == io.EOF {
			break
		}
		res = append(res, rs) //将切片添加到二位切片中
	}
	for _, v := range res {
		//获取年份
		i, err := strconv.Atoi(strings.Split(v[1], "/")[0])
		if err != nil {
			log.Fatal(err)
		}
		j, err := strconv.Atoi(strings.Split(v[0], "/")[0])
		if err != nil {
			log.Fatal(err)
		}
		ii := i - j                     //年份相减
		v = append(v, strconv.Itoa(ii)) //追加到切片v的后面
		f1, err := os.OpenFile("./res.csv", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
		if err != nil {
			log.Fatal(err)
		}
		wr := csv.NewWriter(f1) //创建写对象
		err = wr.Write(v)       //调用写方法,参数为字符串切片
		if err != nil {
			log.Fatal(err)
		}
		wr.Flush() //最后从缓存区写入到文件中
		defer f1.Close()
	}
}

最后的结果为:

处理非预期的域

有时候的csv文件并不都是像上面的那样整洁干净,可能很乱,此时可以通过reader.FieldsPerRecord来处理,FieldsPerRecord说明如下:

FieldsPerRecord是每个记录(行)所需的字段数。
如果FieldsPerRecord为正,则Read要求每个记录具有给定数量的字段。
如果FieldsPerRecord为0,Read将其设置为第一条记录中的字段数,以便将来的记录必须具有相同的字段计数。
如果FieldsPerRecord为负数,则不进行检查制作和记录可能有可变数量的字段

1、修改上述的csv文件,新增4、5字段,如图:

没有添加4、5之前,每个域字段数为8个,现在第三行字段数为10个

再次执行上面的go文件,可以看到报错了,如图:

针对上述错误,有两种解决方法:

第一种,设置FieldsPerRecord的值为负数,不进行检查和记录字段数,如图:

运行结果如下:

第二种,设置域字段FieldsPerRecord数量为8,并获取符合条件的记录数,其余的排除掉,如图:

运行结果如下,已获取到符合要求的数据:

注:如果设置FieldsPerRecord为10,将获取第三条数据,前两条被废弃

二、通过数据帧操作CSV数据

对于复杂的CSV文件,通过标准库encoding/csv处理可能会很麻烦并且效果未必很好,此时可以通过数据帧来操作,本例子演示的相关数据帧程序包为dataframe,地址为:

https://github.com/go-gota/gota
https://pkg.go.dev/github.com/kniren/gota/dataframe#DataFrame

1、定义csv文件,包含列名和字段名,如图:

2、通过dataframe来过滤出IP为192.168.1.1的行,如图:

运行结果如下:

上图中的Filter()方法是可以添加多个F结构体参数的,如果要匹配多个字段,可写为:

3、通过dataframe过滤出IP为192.168.1.1的IP、use、remarks字段,通过Select()方法,如图:

运行结果如下:

Select()方法的只有一个参数,为一个空接口,可以接受任意类型的参数,因此如果要匹配多个字段只能通过切片方式实现

4、更新文件的第3和第4行数据,如图:

运行结果如下:

注意:此数据是读取后更新的,源文件内容没变

更多用法自行参考文档实现!!!

标签