📁

[Golang] csvを操作するための基本的な知識

2022/04/22に公開

Golangでcsvファイルを触るためには 「encoding/csv」 を使っていきます。
https://pkg.go.dev/encoding/csv
他にも標準ライブラリである os を使ってファイルを開いたりします。

そもそもcsvファイルとは?

実務に入って初めてcsvファイルというものに触れたので、そもそもなんなのかを知らなかったので、一応説明しておきます。
csvとは、 Comma Separated Value の略で、意味はそのままですね。カンマ区切りファイルとも呼ばれているそうです。ファイルの拡張子は .csv です。

たとえば郵便局の郵便番号データを見に行くと実際にcsvファイルを入手することができます。
https://www.post.japanpost.jp/zipcode/dl/kogaki-zip.html

csvファイルをNumbersやExcelなどで見てみると以下のような表形式で表示することができます。

csvファイル

csvファイルはプログラミング言語などでごにょごにょすることができるので、互換性や用途に合わせて使える汎用性が高いためとても便利です。

Golangでcsvファイルを読み込む

Read または ReadAll を使うことでファイルを読み込むことができます。

package main

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

func main() {
  file, err := os.Open("28HYOGO.CSV") // 先ほど入手した郵便番号データをos.Openで開く
  if err != nil {
    log.Fatal(err)
  }
  defer file.Close()
  
  r := csv.NewReader(file)
  rows, err := r.ReadAll() // csvを一度に全て読み込む
  if err != nil {
    log.Fatal(err)
  }
  
  // [][]stringなのでループする
  for _, v := range rows {
    fmt.Println(v)
  }
}

Println で標準出力してみると以下のように表示されるかと思います。(SHIFT-JIS形式なのでmacでみると文字化けしていますがひとまず置いておきます)

mattn さんにレスをいただいて上記打ち消し線部分のご指摘いただきました。ありがとうございます。mattnさんの記事は こちら

日本では一般的に CSV ファイルは Shift_JIS でエンコードされている事が多いです。Go 言語は内部のエンコーディングが UTF-8 なので、Shift_JIS な CSV ファイルを読み込むと文字化けします。

[28586 66967 6696746 ˮ��޹� ж���ݼݵݾ���� ��� ���Ɍ� �����S�V���� �˓c 0 0 0 0 0 0]
[28586 66968 6696821 ˮ��޹� ж���ݼݵݾ���� � ���Ɍ� �����S�V���� �� 0 0 0 0 0 0]
[28586 66967 6696744 ˮ��޹� ж���ݼݵݾ���� ֳ�� ���Ɍ� �����S�V���� �p�y 0 0 0 0 0 0]
[28586 66967 6696716 ˮ��޹� ж���ݼݵݾ���� ��� ���Ɍ� �����S�V���� �a�c 0 0 0 0 0 0]
...
...

また、csvを読み込むときは 1度にすべて読み込む(csv.ReadAll) のと 1行ずつ読み込む(csv.Read) 方法があります。
Readを使った場合は以下のようにします。

// 省略

  r := csv.NewReader(file) // csv.NewReaderを使ってcsvを読み込む
  
  // []stringなのでループする
  for {
    row, err := r.Read() // csvを1行ずつ読み込む
      if err == io.EOF {
        break // 読み込むべきデータが残っていない場合,Readはnil, io.EOFを返すため、ここでループを抜ける
      }
    fmt.Println(row)
  }
}

このようにしても同じ結果が得られます。

Golangでcsvファイルへ書き込む

package main

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

func main() {
	records := [][]string{
		[]string{"名前", "年齢", "出身地", "性別"},
		[]string{"山本", "24", "兵庫", "男"},
		[]string{"鈴木", "29", "神奈川", "女"},
		[]string{"佐藤", "27", "鹿児島", "男"},
	}

	f, err := os.Create("test.csv") // 書き込む先のファイル
	if err != nil {
		fmt.Println(err)
	}

	w := csv.NewWriter(f)
	w.WriteAll(records) // 一度にすべて書き込む
}

これを実行すると実行ファイルと同じディレクトリに test.csv というファイルが作成されていおり、その中にrecordsの内容が記載されていると思います。

また Write で書き込む方法は以下です。

package main

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

func main() {
	records := [][]string{
		[]string{"名前", "年齢", "出身地", "性別"},
		[]string{"山本", "24", "兵庫", "男"},
		[]string{"鈴木", "29", "神奈川", "女"},
		[]string{"佐藤", "27", "鹿児島", "男"},
	}

	f, err := os.Create("test.csv")
	if err != nil {
		fmt.Println(err)
	}

	w := csv.NewWriter(f)

	for _, record := range records {
		if err := w.Write(record); err != nil {
			fmt.Println(err)
		}
	}
	w.Flush() // バッファに残っているデータを書き込む
}

まとめ

標準ライブラリを使うことで簡単にcsvファイルを操作できることがわかりました。
他にもBOM付きのcsvファイルにしたり、文字コードをUTF16に変更したりなどの対応が必要になるときもありますので、それについてはまた記事にしたいと思います。

Discussion