【Golang】CSVのDecode

[][]string へのDecodeで我慢できるのであれば、encoding/csv パッケージがそれなりに使える。

  • ファイル終端にいくとEOFする
  • 引用符ありなしを自動解釈
  • 引用符中のダブルクォート2つ重ねを1つにしてくれる
  • 引用符中なら改行文字を何いれてもいい
  • \はエスケープ文字としてみなさない

Decode

package textdata

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

func DecodeCSV(r io.Reader) ([]Textdata, error) {
    rc := csv.NewReader(r)

    datas := make([]Textdata, 0)

    i := 0
    for {
        row, err := rc.Read()
        i = i + 1
        if err != nil {
            if err == io.EOF {
                break
            }
            return nil, err
        }
        if len(row) < 2 {
            return nil, fmt.Errorf("invalid row. too less columns num. [position]: row %d [data]: %s", i, row)
        }
        data := Textdata{}
        data.Caption = row[0]
        data.Body = row[1]
        datas = append(datas, data)
    }

    return datas, nil
}

test

package textdata

import (
    "strings"
    "testing"
)

// Search is a function search textdata in shared repository
func TestDecodeCSV(t *testing.T) {
    gotest := func(csv string, expect []Textdata) {
        r := strings.NewReader(csv)
        datas, err := DecodeCSV(r)
        if err != nil {
            msg := "error occured"
            t.Error(msg)
        }

        if len(expect) != len(datas) {
            t.Errorf("expect %d records, but returns %d.\r\n[expect]:%s\r\n[result]:%s",
                len(expect), len(datas), expect, datas)
            return
        }
    }

    genExpect := func(caption string, body string) Textdata {
        return Textdata{Caption: caption, Body: body, CreateAt: ""}
    }

    gotest(`a,b`, []Textdata{genExpect("a", "b")})
    gotest(`"a",b`, []Textdata{genExpect("a", "b")})
    gotest(`a,"b"`, []Textdata{genExpect("a", "b")})
    gotest(`"""a""","b"`, []Textdata{genExpect("\"a\"", "b")})
    gotest(`"""a\r\nb\rc\nd""","b"`, []Textdata{genExpect("\"a\r\nb\rc\nd\"", "b")})
}