LoginSignup
3
1

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-01-23

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

ざっくりと

こんな感じになる。

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

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

単純に文字列をファイルに書き込む

なんでもいいからファイルに書き込みたいときに使います。

全体の流れ

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

コード①

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 を使えばファイル作成ステップ(①)を省略できる

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

全体の流れ

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

コード

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

全体の流れ

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

コード ①

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)
    }

}

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

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

全体の流れ

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

コード

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)) にも書き込めます

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 ) によると、違いはこのようです。

  • Marshal => String
  • Encode => Stream

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

また、GitHub の Q&A でも、

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

と回答されてますね。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1