使用Go处理CSV文件 | Go主题月

5,128 阅读2分钟

前言

CSV是以逗号为分隔符分割字段,一个csv文件包含零到多条记录,每条记录一到多个字段。每个记录用换行符分隔。

在我们的业务中可能会遇到解析CSV文件的功能,而Go的标准库中也提供了对CSV文件的读写操作。

Reader

type Reader struct {
    Comma            rune 
    Comment          rune 
    FieldsPerRecord  int  
    LazyQuotes       bool 
    TrailingComma    bool 
    TrimLeadingSpace bool 
    // 内含隐藏或非导出字段
}

Comma: 字段分隔符。使用NewReader初始化的Reader对象默认分隔符为逗号。

Comment: 一行开始位置的注释标识符。Comment如果不是0,则表示以Comment声明的注释标识符开始的行会被忽略。

FieldsPerRecord:每条记录期望的字段数。

  • 大于0:当每行的字段超过该值,报错
  • 等于0:这是默认值。检查第一行的字段数量,然后赋值给 FieldsPerRecord
  • 小于0:不检查

LazyQuotes:是否允许懒引号。

TrimLeadingSpace:去除首部的空白符。

// NewReader returns a new Reader that reads from r.
func NewReader(r io.Reader) *Reader {
 return &Reader{
        Comma: ',',
        r:     bufio.NewReader(r),
    }
}

初始化方法的参数为io.Reader类型的接口。如果是从文件读取,可以直接传入os.File。除此之外还可以是各种类型的Reader或者Buffer。

func (r *Reader) Read() (record []string, err error)
func (r *Reader) ReadAll() (records [][]string, err error)
  • Read方法一次读取一行数据,按分隔符将字段分割后,以字符串切片形式返回。
  • ReadAll方法读取CSV的所有内容到内存,返回以每条记录为内容的二维切片。

Writer

type Writer struct {
    Comma   rune 
    UseCRLF bool
    // 内含隐藏或非导出字段
}

Comma:同Reader UseCRLF: true时记录之间使用\r\n分割

// NewWriter returns a new Writer that writes to w.
func NewWriter(w io.Writer) *Writer {
 return &Writer{
        Comma: ',',
        w:     bufio.NewWriter(w),
    }
}

初始化方法和Reader相似,不再展开。

func (w *Writer) Write(record []string) (err error)
func (w *Writer) WriteAll(records [][]string) (err error)

Write和WriteAll与Reader类似,不再展开。

Example

        readCSR := func() error {
		file, err := os.Open("test.csv")
		defer file.Close()
		if err != nil {
			return errors.New(fmt.Sprintf("error to open file: %s", err))
		}

                //case1: 按行读
		reader := csv.NewReader(file)
		for {
			line, err := reader.Read()
			if err == io.EOF {
				fmt.Println("all done")
				break
			}

			if err != nil {
				return errors.New(fmt.Sprintf("error to read file: %s", err))
			}
			fmt.Println("content is: ", line)
		}
		
                //case2: 读全部
		all, err := reader.ReadAll()
                if err != nil {
                    return errors.New(fmt.Sprintf("error to read file: %s", err))
		}
		for idx, line := range all {
			fmt.Println("the ", idx, " line's content is: ", line)
		}
	}