🌊

Golangでフィールド数が可変のCSVを読み込む

2021/08/24に公開

背景

とある銀行の振込結果をインポートする必要がありまして、そのCSVのフォーマットがヘッダーなしで一行目に自身の情報(っぽいやつ)、二行目以降に振込結果が入ってるものだった。
極端だけどこんな感じ。

123,test@example.com,ヤマダタロウ
aaa,bbb,ccc
ddd,eee,fff,ggg
hhh,iii,jjj,kkk,lll

この二行目以降のデータが欲しい。

コード

とりあえず読み込んでみる。

package main

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

func main() {
	file, err := os.Open("test.csv")
	if err == io.EOF {
		log.Fatalln(err)
	}
	r := csv.NewReader(file)

	// 一行目を読み飛ばす
	_, err = r.Read()
	if err == io.EOF {
		log.Fatalln(err)
	}

	for {
		record, err := r.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatalln(err)
		}

		fmt.Println(record)
	}
}

実行

$ go run main.go
[aaa bbb ccc]
2021/08/23 23:33:54 record on line 3: wrong number of fields
exit status 1

どうやらフィールドの数が揃ってないとエラーになるらしい。困った。

パッケージのドキュメントを読んでみる

encoding/csv のドキュメント
https://pkg.go.dev/encoding/csv

すると、type Readerにこんなものを発見

type Reader struct {
	...
	// FieldsPerRecord is the number of expected fields per record.
	// If FieldsPerRecord is positive, Read requires each record to
	// have the given number of fields. If FieldsPerRecord is 0, Read sets it to
	// the number of fields in the first record, so that future records must
	// have the same field count. If FieldsPerRecord is negative, no check is
	// made and records may have a variable number of fields.
	FieldsPerRecord int
	...
}

こいつに負の値を入れたらうまいことやってくれそうな雰囲気。

試してみる

func main() {
	file, err := os.Open("test.csv")
	if err == io.EOF {
		log.Fatalln(err)
	}
	r := csv.NewReader(file)
	r.FieldsPerRecord = -1 // 追加

	// 一行目を読み飛ばす
	_, err = r.Read()
	if err == io.EOF {
		log.Fatalln(err)
	}

	for {
		...
	}
}

実行

$ go run main.go
[aaa bbb ccc]
[ddd eee fff ggg]
[hhh iii jjj kkk lll]

読めた読めた。

Discussion