goでデータベースから取得したデータをcsvにして出力する
最近業務で大量のデータをcsvに出力する必要があり、goでのcsv出力方法を調べたのでまとめます。
goでcsv出力のサンプル
goでcsvを出力するために「encoding/csv」が用意されています。
参考:Package csv
「encoding/csv」を使って簡単なcsvを出力する公式ドキュメントサンプルは以下です。
package main
import (
"encoding/csv"
"log"
"os"
)
func main() {
records := [][]string{
{"first_name", "last_name", "username"},
{"Rob", "Pike", "rob"},
{"Ken", "Thompson", "ken"},
{"Robert", "Griesemer", "gri"},
}
w := csv.NewWriter(os.Stdout)
for _, record := range records {
if err := w.Write(record); err != nil {
log.Fatalln("error writing record to csv:", err)
}
}
// Write any buffered data to the underlying writer (standard output).
w.Flush()
if err := w.Error(); err != nil {
log.Fatal(err)
}
}
この例では2次元の文字列型のスライスを用意し、 csv.NewWriter にio.Writerを 実装している標準出力「os.Stdout」を渡してcsvのWriterを取得しています。 forでスライスを1行ずつ w.Write で書き込み、最後に w.Flush でバッファに 残っているデータをすべて書き込みます。
実行結果は以下です。
$ go run main.go
first_name,last_name,username
Rob,Pike,rob
Ken,Thompson,ken
Robert,Griesemer,gri
この例では1行ずつ書き込んでいますが上記の例のようにあらかじめ2次元の スライスがある場合には WriteAll でまとめて出力することも可能です。
package main
import (
"encoding/csv"
"log"
"os"
)
func main() {
records := [][]string{
{"first_name", "last_name", "username"},
{"Rob", "Pike", "rob"},
{"Ken", "Thompson", "ken"},
{"Robert", "Griesemer", "gri"},
}
w := csv.NewWriter(os.Stdout)
w.WriteAll(records) // calls Flush internally
if err := w.Error(); err != nil {
log.Fatalln("error writing csv:", err)
}
}
このようにencoding/csvは簡単に利用できます。
csv.NewWriter にはio.Writerを実装していればなんでも渡せるので、 以下のようにFileを渡せばcsvをファイルに書きだせます。file, err := os.Create("sample.csv")
if err != nil {
panic(err)
}
defer file.Close()
cw := csv.NewWriter(file)
defer cw.Flush()
encoding/csvで指定できるパラメータ
csv.NewWriter で返されるWriter構造体は以下のように定義されています。// A Writer writes records using CSV encoding.
//
// As returned by NewWriter, a Writer writes records terminated by a
// newline and uses ',' as the field delimiter. The exported fields can be
// changed to customize the details before the first call to Write or WriteAll.
//
// Comma is the field delimiter.
//
// If UseCRLF is true, the Writer ends each output line with \r\n instead of \n.
//
// The writes of individual records are buffered.
// After all data has been written, the client should call the
// Flush method to guarantee all data has been forwarded to
// the underlying io.Writer. Any errors that occurred should
// be checked by calling the Error method.
type Writer struct {
Comma rune // Field delimiter (set to ',' by NewWriter)
UseCRLF bool // True to use \r\n as the line terminator
w *bufio.Writer
}
デフォルトで区切り文字は「,」が設定されており、区切り文字を変えたい場合は以下の用にCommaにruneを設定します。
w := csv.NewWriter(file)
w.Comma = '|'
また、改行文字をCRLF(\r\n)にする場合は以下のようにUseCRLFにtrueを設定します。
w := csv.NewWriter(file)
w.UseCRLF = true
データベースから取得したデータをcsvに出力してみる
今回はサンプルとして以下のテーブルを用意しました。
mysql> desc users;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| first_name | varchar(30) | NO | | NULL | |
| last_name | varchar(30) | NO | | NULL | |
| tel | varchar(30) | NO | | NULL | |
| email | varchar(200) | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
上記にテーブルにfakerなどを使って適当にサンプルデータを入れておきます。 usersテーブルの内容をcsvで出力するgoのサンプルコードは以下になります。
package main
import (
"database/sql"
"encoding/csv"
"fmt"
"os"
_ "github.com/go-sql-driver/mysql"
)
func main() {
var db *sql.DB
var err error
db, err = sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", "ユーザ名", "パスワード", "ホスト", "ポート番号", "db名"))
if err != nil {
panic(err)
}
defer db.Close()
err = db.Ping()
if err != nil {
panic(err)
}
file, err := os.Create("sample.csv")
if err != nil {
panic(err)
}
defer file.Close()
cw := csv.NewWriter(file)
defer cw.Flush()
var id int
var firstName, lastName, tel, email string
rows, err := db.Query("select * from users")
if err != nil {
panic(err)
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&id, &firstName, &lastName, &tel, &email)
if err != nil {
panic(err)
}
col := []string{firstName, lastName, tel, email}
cw.Write(col)
}
err = rows.Err()
if err != nil {
panic(err)
}
}
Flushを忘れてはいけないので csv.NewWriter を呼び出した直後にdeferでFlushを指定しておきました。
上記を実行すると「sample.csv」ファイルが作成されます。
作成されたファイルをみてみると・・・
$ go run main.go
$ cat sample.csv
Damaris,Eichmann,821-647-1093,bpYZUis@YfomxEY.net
Damaris,Eichmann,821-647-1093,bpYZUis@YfomxEY.net
Damaris,Eichmann,821-647-1093,bpYZUis@YfomxEY.net
Damaris,Eichmann,821-647-1093,bpYZUis@YfomxEY.net
Damaris,Eichmann,821-647-1093,bpYZUis@YfomxEY.net
Damaris,Eichmann,821-647-1093,bpYZUis@YfomxEY.net
Damaris,Eichmann,821-647-1093,bpYZUis@YfomxEY.net
Damaris,Eichmann,821-647-1093,bpYZUis@YfomxEY.net
・・・・
csvが出力されました。
今回はutf8でそのまま出力していますが、データに日本語を含みcsvをエクセルで開きたい場合はShiftJISで出力する必要があります。
その場合はNewWriterに渡す前にtransformで文字コードを指定します。
また改行コードもCRLFを指定します。
利用する為にインストールします
$ go get golang.org/x/text/encoding/japanese
$ go get golang.org/x/text/transform
w := csv.NewWriter(transform.NewWriter(file, japanese.ShiftJIS.NewEncoder()))
w.UseCRLF = true
おわりに
encoding/csvを利用して効率よくcsvが出力できるのでとても便利でした。
csv.NewWriterに http.ResponseWriter を渡してそのままダウンロードさせることも可能なので今後もいろいろとio.Writerを組み合わせて利用していきたいと思います。
この記事を書いた人
- 創造性を最大限に発揮するとともに、インターネットに代表されるITを活用し、みんなの生活が便利で、豊かで、楽しいものになるようなサービスやコンテンツを考え、創り出し提供しています。
この執筆者の最新記事
関連記事
最新記事
FOLLOW US
最新の情報をお届けします
- facebookでフォロー
- Twitterでフォロー
- Feedlyでフォロー