[Go] データをファイルに書き込む方法をまとめる

色んな書き込み方があり、何かとごちゃまぜになったので、一旦まとめてみました。
パターンがあり、いつ何を使えば良いか見えてきたので、同じく混乱している方の参考になればと思います。

## ざっくりと

こんな感じになる。

1. __書き込み先を決める__ (例: file, buffer)。ファイルの場合 `defer Close()` を忘れずに
2. __データを変換する__ (例: []byte, Marshal), または __書き込むやつを取得する__ (例: Writer, Encoder)
3. __書き込む__ (例: Write, Encode)

データ格納先へのデータの渡し方が多少違うので、覚えてなければ、都度マニュアルを確認しましょ。

## 単純に文字列をファイルに書き込む
なんでもいいからファイルに書き込みたいときに使います。

### 全体の流れ
1. 書き込み先のファイル作成 (`f := os.Create`)
2. バイト文字列に変換 (`d := []byte{"文字列"}`)
3. 書き込み (`f.Write(d)`)

### コード①

```golang
package main

import (
	"fmt"
	"log"
	"os"
)

func main() {

	// 1. 書き込み先のファイル作成
	f, err := os.Create("test.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	// 2. バイト文字列に変換
	d := []byte("バイト文字列に変換")

	// 3. 書き込み
	n, err := f.Write(d)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%d bytes 書き込んだよ!", n)
}
```

### コード②: `ioutil.WriteFile` を使えばファイル作成ステップ(①)を省略できる

```golang
package main

import (
	"io/ioutil"
	"log"
)

func main() {

	// 2. バイト文字列に変換
	d := []byte("バイト文字列に変換")

	// 3. 書き込み
	err := ioutil.WriteFile("test.txt", d, 0644)
	if err != nil {
		log.Fatal(err)
	}
}
```

## ライター (`Writer`) を使って書き込む
bufio や CSV で使います。
[参考: NewWriter](https://golang.org/search?q=NewWriter)

### 全体の流れ

1. __書き込み先のファイル生成__ (`f := os.Create`)
2. __生成したファイルに書き込む、ライターを取得__ (`w := NewWriter(f)`)
    1. パッケージによっては、`defer w.Flush()` を忘れずに (例: bufio & CSV)
3. __書き込み__ (`w.Write()`)
    1. CSV の場合、引数は `[]string` を渡す。
    1. Bufio の場合、引数は `[]byte` を渡す。ただ `WriteString` で `[]string` を渡せる

### コード

```golang
package main

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

func main() {

	data := [][]string{
		{"name", "age"},
		{"田中", "25"},
		{"加藤", "30"},
	}

	// 1. 書き込み先のファイル生成
	f, err := os.Create("test.csv")
	if err != nil {
		log.Fatalln("failed to open file", err)
	}
	defer f.Close()

	// 2. ライターを取得
	w := csv.NewWriter(f)
	defer w.Flush()

	// 3. 書き込み
	for _, record := range data {
		err := w.Write(record)
		if err != nil {
			log.Fatal(err)
		}
	}
}
```

## Encoder を使って書き込む

JSON や XML で使います。
[参考: NewEncoder](https://golang.org/search?q=NewEncoder)


### 全体の流れ

1. __書き込み先のファイル生成__ (`f := os.Create("test.json")`)
2. __ファイルに書き込む、エンコーダを取得__ (`encoder := json.newEncoder(f)`)
3. __構造体を JSON データにエンコードする__ (`encoder.Encode(&data)`)

### コード ①

```golang
package main

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

type User struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {

	data := []User{
		User{Name: "田中", Age: 25},
		User{Name: "加藤", Age: 30},
	}

	// 1. 書き込み先のファイル生成
	f, err := os.Create("test.json")
	if err != nil {
		log.Fatal(err)
	}

	// 2. ファイルに書き込む、エンコーダを取得
	encoder := json.NewEncoder(f)

	// 3. data をファイルに書き込み
	err = encoder.Encode(&data)
	if err != nil {
		log.Fatal(err)
	}

}
```

### コード ②: エンコーダーの取得と書き込み、一連の流れで書ける

```golang
package main

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

type User struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {

	data := []User{
		User{Name: "田中", Age: 25},
		User{Name: "加藤", Age: 30},
	}

	// 1. 書き込み先のファイル生成
	f, err := os.Create("test.json")
	if err != nil {
		log.Fatal(err)
	}

	// 2. ファイルに書き込む、エンコーダを取得 & 3 書き込み
	err = json.NewEncoder(f).Encode(&data)
	if err != nil {
		log.Fatal(err)
	}
}
```

## Marshal を使って書き込む

JSON や XML で使います。

[参考: Marshal](https://golang.org/search?q=Marshal)

### 全体の流れ

1. __書き込み先のファイル生成__ (`f := os.Create("test.json)`)
2. __構造体を JSON データに変換する__ ( `output, _ := json.Marshal(&data)`)
3. __データを書き込む__ (`f.Write(output)`)

### コード
```golang
import (
	"encoding/json"
	"fmt"
	"log"
	"os"
)

type User struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {

	data := []User{
		User{Name: "田中", Age: 25},
		User{Name: "加藤", Age: 30},
	}

	// 1. 書き込み先のファイル生成
	f, err := os.Create("test.json")
	if err != nil {
		log.Fatal(err)
	}

	// 2. バイト文字列の JSON データに変換
	output, err := json.Marshal(&data)
	if err != nil {
		log.Fatal(err)
	}

	// 3. data を書き込み
	n, err := f.Write(output)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%d bytes 書き込んだよ!", n)
}
```

## 番外編: 書き込み先がバッファの場合

もちろん、ファイルだけでなくバッファ (`buf := new(bytes.Buffer)`) にも書き込めます

```golang
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
)

type User struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {

	data := []User{
		User{Name: "田中", Age: 25},
		User{Name: "加藤", Age: 30},
	}

	// 1. 書き込み先のバッファを生成
	buf := new(bytes.Buffer)

	// 2. バッファに書き込む、エンコーダを取得
	encoder := json.NewEncoder(buf)

	// 3. data をバッファに書き込む
	err := encoder.Encode(&data)
	if err != nil {
		log.Fatal(err)
	}
}
```

## 番外編: Encode vs Marshal どっちを使うべきか?

JSON や XML は共に、Encode と Marshal が使え、同じ結果が得られます。
では違いはなんなのでしょうか？

Stack Overflow( [参考: in Golang, what is the difference between json encoding and marshalling](https://stackoverflow.com/questions/33061117/in-golang-what-is-the-difference-between-json-encoding-and-marshalling) ) によると、違いはこのようです。

* Marshal => String
* Encode => Stream

使い分けとして、文字列やバイト文字としてして扱いたい場合は Marshal、それ以外では Encode を使うようです。

また、[GitHub](https://github.com/capnproto/go-capnproto2/issues/43) の Q&A でも、

* (メモリ上の) バイト文字列を取り扱う場合は、Marshal
* Reader/Writer (ファイルなどの stream) を取り扱う場合は、Encode

と回答されてますね。