首页 > 编程笔记 > Go语言笔记 阅读:12

Go语言csv包读写CSV文件(非常详细)

CSV(Comma-Separated Values,逗号分隔值)文件以纯文本形式存储表格数据(数字和文本),每行数据的各个字段使用逗号或制表符分隔,支持 Excel 和记事本打开,如下图所示。


图 1 CSV文件内容

内置包 encoding/csv 提供 4 种方式读写 CSV 文件:按行读取、全部读取、按行写入和全部写入,分别由 Read()、ReadAll()、Write() 和 WriteAll() 实现。

CSV 文件数据写入示例如下:
package main

import (
   "encoding/csv"
   "fmt"
   "os"
)

func main() {
   // OpenFile()创建或打开文件,设置读写模式
   // 如果设置O_APPEND模式,则实现文件续写功能
   // 如果设置O_TRUNC模式,则新数据覆盖文件原有数据
   nfs, _:=os.OpenFile("input.csv",os.O_RDWR|os.O_CREATE|os.O_APPEND,0)
   // 将文件对象nfs加载到NewWriter(),实例化结构体Writer
   csvWriter := csv.NewWriter(nfs)
   // 设置结构体Writer的成员
   // Comma设置每个字段之间的分隔符,默认为逗号
   csvWriter.Comma = ','
   // UseCRLF默认为true,使用\r\n作为换行符
   csvWriter.UseCRLF = true

   // 写入一行数据
   row := []string{"1", "2", "3", "4"}
   err := csvWriter.Write(row)
   if err != nil {
        fmt.Printf("无法写入,错误信息:%v\n", err)
   }

   // 一次性写入多行数据
   var newContent [][]string
   newContent = append(newContent, []string{"11", "12", "13", "14"})
   newContent = append(newContent, []string{"21", "22", "23", "24"})
   csvWriter.WriteAll(newContent)

   // 将数据写入文件
   csvWriter.Flush()
   // 关闭文件
   nfs.Close()
}
上述代码的实现过程说明如下:
1) 使用 os 包创建或打开文件 input.csv,如果设置 O_APPEND 模式,文件支持数据续写功能;如果设置 O_TRUNC 模式,当前写入数据将覆盖文件原有数据。

2) 将 os 包创建或打开的文件作为 encoding/csv 包 NewWriter() 的参数,得到结构体 Writer 的实例化对象,并允许修改结构体成员 Comma 和 UseCRLF 的值,一般情况下结构体成员使用默认值即可。

3) 由结构体 Writer 实例化对象调用结构体方法 Write() 或 WriteAll() 实现数据写入操作:
结构体方法 Write() 表示写入一行数据,参数 record 是字符串类型的切片,切片元素表示一行数据的某个字段;
结构体方法 WriteAll() 表示一次写入多行数据,参数 records 是字符串类型的二维切片,由于每一行数据以一个切片表示,多行数据是将多个切片放在一个新的切片中,从而组成了二维切片。

4) 数据写入之后必须由结构体 Writer 实例化对象调用结构体方法 Flush() 将数据写入文件,程序结束之后,文件才会生成数据,再由 os 包创建或打开的文件调用 Close() 关闭文件,释放资源。

CSV 文件读取分为按行读取和全部读取:
两者的应用示例如下:
package main

import (
   "encoding/csv"
   "fmt"
   "io"
   "os"
)

func main() {
   // OpenFile()创建或打开文件,设置读写模式
   // O_RDWR已经支持文件读写操作
   // O_CREATE当文件不存在时会自动创建文件
   fs, _ := os.OpenFile("input.csv", os.O_RDWR|os.O_CREATE, 0)
   // 将文件对象fs加载到NewReader(),实例化结构体Reader
   csvReader := csv.NewReader(fs)
   // 一行一行地读取文件,常用于大文件
   for {
        // 调用结构体方法Read()读取文件内容
        row, err := csvReader.Read()
        if err == io.EOF || err != nil {
             break
        }
        fmt.Printf("Read()读取CSV内容:%v,数据类型:%T\n",row,row)
   }
   // 关闭文件
   fs.Close()

   // 一次性读取文件所有内容,常用于小文件
   fs1, _ := os.OpenFile("input.csv", os.O_RDWR|os.O_CREATE, 0)
   // 将文件对象fs1加载到NewReader(),实例化结构体Reader
   csvReader1 := csv.NewReader(fs1)
   // 调用结构体方法ReadAll()读取文件所有内容
   content, err := csvReader1.ReadAll()
   if err != nil {
        fmt.Printf("ReadAll()读取失败:%v\n", err)
   }
   // 遍历输出每一行数据
   for _, row := range content {
        fmt.Printf("ReadAll()读取CSV内容:%v,数据类型:%T\n",row,row)
   }
   // 关闭文件
   fs1.Close()
}
运行上述代码,运行结果为:

Read()读取CSV内容:[1 2 3 4],数据类型:[]string
Read()读取CSV内容:[11 12 13 14],数据类型:[]string
Read()读取CSV内容:[21 22 23 24],数据类型:[]string
ReadAll()读取CSV内容:[1 2 3 4],数据类型:[]string
ReadAll()读取CSV内容:[11 12 13 14],数据类型:[]string
ReadAll()读取CSV内容:[21 22 23 24],数据类型:[]string

CSV 文件的按行读取和全部读取的实现过程如下:
1) 使用 os 包创建或打开文件 input.csv,文件模式只需设置 O_RDWR 和 O_CREATE,O_RDWR 支持文件读写,O_CREATE 在文件不存在的时候能自动创建文件。

2) 将 os 包创建或打开的文件作为 encoding/csv 包 NewReader() 的参数,得到结构体 Reader 的实例化对象。

3) 如果结构体 Reader 实例化对象调用 Read() 方法,程序将实现按行读取,由于数据行数无法确定,因此使用 for 死循环方式读取,当读取结果 err 不为空值 nil(说明读取出现异常)或等于 io.EOF(io.EOF 代表文件末端,说明数据已读完)时将终止循环,读取结果 row 是每次循环读取的数据,即文件的每一行数据。

4) 如果结构体 Reader 实例化对象调用 ReadAll() 方法,程序将实现全部读取,若读取结果 err 不为空值 nil(说明读取出现异常),则输出异常提示,通过 for…range 遍历读取文件的每一行数据。

综上所述,内置包 encoding/csv 实现 CSV 文件读写的操作如下:
1) 首先使用 os 包创建或打开 CSV 文件,如果实现数据写入,可根据实际需要设置 O_APPEND 或 O_TRUNC 模式;如果实现数据读取,只需设置 O_RDWR 和 O_CREATE 模式。

2) 然后将 os 包创建或打开的文件对象作为 encoding/csv 的 NewReader() 或 NewWriter() 的参数,得到对应结构体实例化对象,再由结构体实例化对象调用 Read()、ReadAll() 或 Write()、WriteAll() 实现文件的读取或写入操作。

3)最后由结构体实例化对象调用 Flush() 将数据写入文件,或者通过循环遍历输出文件的每一行数据。

相关文章