いろいろ備忘録日記

主に .NET とか Go とか Python絡みのメモを公開しています。

Goメモ-153 (Goでのファイル I/O のやり方まとめ)

概要

以下、自分用のメモです。よく忘れるのでメモメモ。

他の言語で開発した後にGo触ると、たまに「この場合どうやるんだっけ??」ってなるので、自分用にここにメモメモ。

サンプル自体は以下のリポジトリにアップしていますので、よければご参照ください。

github.com

ファイル開いて読み込み

package readwrite

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

// OpenRead は、ファイルをOpenしてReadするサンプルです.
func OpenRead() error {
    // ファイルを操作する場合は os パッケージを利用する
    filename := "README.md"
    file, err := os.OpenFile(filename, os.O_RDONLY, 0444)
    if err != nil {
        log.Fatal(err)
        return err
    }

    // GO言語ではIO周りのデータは文字列ではなくバイト列として扱うのみ
    buf := make([]byte, 1024)
    n, err := file.Read(buf)
    if err != nil {
        log.Fatal(err)
        return err
    }

    // バイト列を文字列にしたい場合は string([]byte) を使う
    fmt.Printf("%s", string(buf[:n]))

    return nil
}

ファイル開いて読み込み2

package readwrite

import (
    "bytes"
    "io"
    "os"

    "github.com/devlights/gomy/output"
)

// OpenRead2 は、os.Openを使ったファイルを読み込みのサンプルです.
func OpenRead2() error {
    var (
        f   io.ReadCloser
        err error
    )

    if f, err = os.Open("README.md"); err != nil {
        return err
    }
    defer f.Close()

    var (
        data []byte
    )

    if data, err = io.ReadAll(f); err != nil {
        return err
    }

    line := data[:bytes.Index(data, []byte("\n"))]
    output.Stdoutl(string(line))

    return nil
}

ファイル開いて書き込み

package readwrite

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

// OpenWrite は、ファイルをOpenしてWriteするサンプルです.
func OpenWrite() error {
    // 一時ファイルの作成
    file, err := os.CreateTemp("", "example")
    if err != nil {
        log.Fatal(err)
        return err
    }

    fmt.Printf("TmpFile: %s\n", file.Name())

    // 関数を抜けた際に、利用したファイルを削除
    defer func() {
        _ = os.Remove(file.Name())

        // ファイルの存在チェックは、Go言語ではこうやる
        // REF: http://bit.ly/2I1LzYa
        if _, err = os.Stat(file.Name()); os.IsNotExist(err) {
            fmt.Println("存在しない")
        }
    }()

    // ファイル存在するか確認
    _, err = os.Stat(file.Name())
    if err == nil {
        fmt.Println("ファイル存在する")
    }

    // データを書き込む
    message := "hello world"
    _, err = file.WriteString(message)
    if err != nil {
        log.Fatal(err)
        return err
    }

    // 閉じる
    err = file.Close()
    if err != nil {
        log.Fatal(err)
        return err
    }

    // 読み出してみる
    data, err := os.ReadFile(file.Name())
    if err != nil {
        log.Fatal(err)
        return err
    }

    message = string(data)
    fmt.Println(message)

    return nil
}

ファイル開いて書き込み2

package readwrite

import (
    "fmt"
    "io"
    "os"

    "github.com/devlights/gomy/output"
)

// OpenWrite2 は、os.Create を使ったサンプルです.
func OpenWrite2() error {
    const (
        fpath = "/tmp/trygolang-openwrite2.txt"
    )

    var (
        f   io.WriteCloser
        err error
    )

    if f, err = os.Create(fpath); err != nil {
        return err
    }

    func() {
        defer f.Close()

        fmt.Fprintln(f, "\nhello world")
        fmt.Fprintln(f, "world hello")
    }()

    var (
        data []byte
    )

    if data, err = os.ReadFile(fpath); err != nil {
        return err
    }

    output.Stdoutl("[data]", string(data))

    return nil
}

追記モード (Append) で開いて書き込み

package readwrite

import (
    "fmt"
    "io"
    "os"

    "github.com/devlights/gomy/output"
)

// OpenAppend は、追記モードでファイルを開くサンプルです。
func OpenAppend() error {
    const (
        fpath = "/tmp/trygolang-openappend.txt"
    )

    write := func(fpath string) error {
        var (
            f     io.WriteCloser
            err   error
            flgs  int         = os.O_APPEND | os.O_CREATE | os.O_WRONLY
            fmode os.FileMode = 0644
        )

        if f, err = os.OpenFile(fpath, flgs, fmode); err != nil {
            return err
        }
        defer f.Close()

        fmt.Fprintln(f, "hello world")

        return nil
    }

    var (
        err error
    )

    // 2回書き込み
    for range make([]struct{}, 2) {
        if err = write(fpath); err != nil {
            return err
        }
    }

    // 結果確認
    var (
        r io.ReadCloser
    )

    if r, err = os.Open(fpath); err != nil {
        return err
    }
    defer r.Close()

    var (
        data []byte
    )

    if data, err = io.ReadAll(r); err != nil {
        return err
    }

    output.Stdoutl("[append]", string(data))

    return nil
}

/dev/null のように出力を捨てる

少し古いサンプルなので、ioutil.Discard を使っていますが、今のバージョンでは io.Discard を使うべきですね。

golang.org

package readwrite

import (
    "io"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "sync"
    "time"
)

// NullWriter -- ioutil.Discard のサンプルです.
//
// REFERENCES:
//   - https://golang.org/io/ioutil/#pkg-variables
//   - https://stackoverflow.com/a/25344458
func NullWriter() error {
    // ----------------------------------------------------------------
    // ioutil.Discard は、io.Writer を実装しているけど何もしません。
    // 処理を行う上で io.Writer が必要だが、その結果は必要ない場合などに利用します。
    // ----------------------------------------------------------------
    var (
        urls = []string{
            "https://www.alexa.com/topsites/", // このサイトは大きいので取得に時間がかかる
            "https://www.google.co.jp/",
            "https://golang.org/",
            "https://github.com/",
        }
    )

    var (
        logger = log.New(os.Stdout, "", 0)
        wg     sync.WaitGroup
        errCh  = make(chan error, len(urls))
    )

    var (
        fetch = func(wg *sync.WaitGroup, url string) {
            defer wg.Done()
            defer func(start time.Time) {
                logger.Printf("fetch: %-40s --> %v\n", url, time.Since(start))
            }(time.Now())

            var (
                client = http.Client{
                    Timeout: 1 * time.Second,
                }
            )

            resp, err := client.Get(url)
            if err != nil {
                errCh <- err
                return
            }

            defer func() {
                if err = resp.Body.Close(); err != nil {
                    errCh <- err
                }
            }()

            // 結果は必要ないので捨てる
            _, err = io.Copy(ioutil.Discard, resp.Body)
            if err != nil {
                errCh <- err
                return
            }
        }
    )

    start := time.Now()
    for _, url := range urls {
        wg.Add(1)
        go fetch(&wg, url)
    }

    wg.Wait()
    close(errCh)

    for e := range errCh {
        logger.Printf("Error: %v\n", e)
    }

    logger.Printf("elapsed: %v\n", time.Since(start))

    return nil
}

Shift-JIS なファイルを読み込み

package sjis

import (
    "bytes"
    "fmt"
    "io"
    "os"
    "os/exec"

    "github.com/devlights/gomy/output"
    "golang.org/x/text/encoding"
    "golang.org/x/text/encoding/japanese"
    "golang.org/x/text/transform"
)

// Read は、Shift-JISなテキストを読み込むサンプルです.
//
// golang.org/x/text/encoding/japanese を利用します.
func Read() error {
    // -------------------------------------------------------------
    // Go で、日本語特有のエンコーディング (sjis や euc-jp など) を扱う場合
    // 以下のパッケージを利用する.
    //
    //   golang.org/x/text/encoding/japanese
    //
    // インストールは、普通に go get するだけ。
    //   go get golang.org/x/text/encoding/japanese
    // go get すると、 golang.org/x/text が依存関係として追加される.
    //
    // japanese.ShiftJIS が sjis, japanese.EUCJP が euc-jp に
    // 対応している。どちらも Encoder と Decoder を持っているので
    // それを transform.NewXXX に渡すことにより、*io.Reader or *io.Writer を
    // 取得することができる。
    //
    // transform.NewReader に渡すのが Decoder
    // transform.NewWriter に渡すのが Encoder
    //
    // となる。
    // -------------------------------------------------------------
    const (
        shell = "/usr/bin/bash"
        fpath = "/tmp/sjis.txt"
    )

    // 先にサンプルとなる Shift-JIS エンコーディング のファイルを作成しておく
    var (
        cmd    *exec.Cmd
        err    error
        cmdTxt = fmt.Sprintf("echo 'こんにちわ世界' | nkf -sd > %s", fpath)
    )

    cmd = exec.Command(shell, "-c", cmdTxt)
    if err = cmd.Run(); err != nil {
        return err
    }

    // 対象ファイルのデータを読み出し
    var (
        buf       *bytes.Buffer
        origBytes []byte
    )

    if origBytes, err = os.ReadFile(fpath); err != nil {
        return err
    }
    buf = bytes.NewBuffer(origBytes)

    // Shift-JIS で デコード
    var (
        decoder    *encoding.Decoder = japanese.ShiftJIS.NewDecoder()
        sjisReader *transform.Reader = transform.NewReader(buf, decoder)
        sjisBytes  []byte
    )

    if sjisBytes, err = io.ReadAll(sjisReader); err != nil {
        return err
    }

    // 結果出力
    output.Stdoutl("[original]", string(origBytes))
    output.Stdoutl("[shiftjis]", string(sjisBytes))

    return nil
}

Shift-JISなファイルを書き込み

package sjis

import (
    "bytes"
    "io"

    "github.com/devlights/gomy/output"
    "golang.org/x/text/encoding"
    "golang.org/x/text/encoding/japanese"
    "golang.org/x/text/transform"
)

// Write は、Shift-JISなテキストを書き込むサンプルです.
//
// golang.org/x/text/encoding/japanese を利用します.
func Write() error {
    // -------------------------------------------------------------
    // Go で、日本語特有のエンコーディング (sjis や euc-jp など) を扱う場合
    // 以下のパッケージを利用する.
    //
    //   golang.org/x/text/encoding/japanese
    //
    // インストールは、普通に go get するだけ。
    //   go get golang.org/x/text/encoding/japanese
    // go get すると、 golang.org/x/text が依存関係として追加される.
    //
    // japanese.ShiftJIS が sjis, japanese.EUCJP が euc-jp に
    // 対応している。どちらも Encoder と Decoder を持っているので
    // それを transform.NewXXX に渡すことにより、*io.Reader or *io.Writer を
    // 取得することができる。
    //
    // transform.NewReader に渡すのが Decoder
    // transform.NewWriter に渡すのが Encoder
    //
    // となる。
    // -------------------------------------------------------------

    // 書き出し対象データを用意
    var (
        original = []byte("こんにちわ世界")
        buf      = bytes.NewBuffer(original)
    )

    // Shift-JIS の エンコーダー 経由で書き出し
    var (
        sjisBuf    = new(bytes.Buffer)
        err        error
        encoder    *encoding.Encoder = japanese.ShiftJIS.NewEncoder()
        sjisWriter *transform.Writer = transform.NewWriter(sjisBuf, encoder)
    )

    if _, err = io.Copy(sjisWriter, buf); err != nil {
        return err
    }

    // 結果確認
    output.Stdoutl("[original]", string(original))
    output.Stdoutl("[sjis    ]", sjisBuf.String())

    return nil
}

EUC-JP なファイルを読み込み

package eucjp

import (
    "bytes"
    "fmt"
    "io"
    "os"
    "os/exec"

    "github.com/devlights/gomy/output"
    "golang.org/x/text/encoding"
    "golang.org/x/text/encoding/japanese"
    "golang.org/x/text/transform"
)

// Read は、EUC-JP なテキストを読み込むサンプルです.
//
// golang.org/x/text/encoding/japanese を利用します.
func Read() error {
    // -------------------------------------------------------------
    // Go で、日本語特有のエンコーディング (sjis や euc-jp など) を扱う場合
    // 以下のパッケージを利用する.
    //
    //   golang.org/x/text/encoding/japanese
    //
    // インストールは、普通に go get するだけ。
    //   go get golang.org/x/text/encoding/japanese
    // go get すると、 golang.org/x/text が依存関係として追加される.
    //
    // japanese.ShiftJIS が sjis, japanese.EUCJP が euc-jp に
    // 対応している。どちらも Encoder と Decoder を持っているので
    // それを transform.NewXXX に渡すことにより、*io.Reader or *io.Writer を
    // 取得することができる。
    //
    // transform.NewReader に渡すのが Decoder
    // transform.NewWriter に渡すのが Encoder
    //
    // となる。
    // -------------------------------------------------------------
    const (
        shell = "/usr/bin/bash"
        fpath = "/tmp/eucjp.txt"
    )

    // 先にサンプルとなる EUC-JP エンコーディング のファイルを作成しておく
    var (
        cmd    *exec.Cmd
        err    error
        cmdTxt = fmt.Sprintf("echo 'こんにちわ世界' | nkf -ed > %s", fpath)
    )

    cmd = exec.Command(shell, "-c", cmdTxt)
    if err = cmd.Run(); err != nil {
        return err
    }

    // 対象ファイルのデータを読み出し
    var (
        buf       *bytes.Buffer
        origBytes []byte
    )

    if origBytes, err = os.ReadFile(fpath); err != nil {
        return err
    }
    buf = bytes.NewBuffer(origBytes)

    // Shift-JIS で デコード
    var (
        decoder   *encoding.Decoder = japanese.EUCJP.NewDecoder()
        eucReader *transform.Reader = transform.NewReader(buf, decoder)
        eucBytes  []byte
    )

    if eucBytes, err = io.ReadAll(eucReader); err != nil {
        return err
    }

    // 結果出力
    output.Stdoutl("[original]", string(origBytes))
    output.Stdoutl("[euc-jp  ]", string(eucBytes))

    return nil
}

EUC-JP なファイルを書き込み

package eucjp

import (
    "bytes"
    "io"

    "github.com/devlights/gomy/output"
    "golang.org/x/text/encoding"
    "golang.org/x/text/encoding/japanese"
    "golang.org/x/text/transform"
)

// Write は、EUC-JP なテキストを書き込むサンプルです.
//
// golang.org/x/text/encoding/japanese を利用します.
func Write() error {
    // -------------------------------------------------------------
    // Go で、日本語特有のエンコーディング (sjis や euc-jp など) を扱う場合
    // 以下のパッケージを利用する.
    //
    //   golang.org/x/text/encoding/japanese
    //
    // インストールは、普通に go get するだけ。
    //   go get golang.org/x/text/encoding/japanese
    // go get すると、 golang.org/x/text が依存関係として追加される.
    //
    // japanese.ShiftJIS が sjis, japanese.EUCJP が euc-jp に
    // 対応している。どちらも Encoder と Decoder を持っているので
    // それを transform.NewXXX に渡すことにより、*io.Reader or *io.Writer を
    // 取得することができる。
    //
    // transform.NewReader に渡すのが Decoder
    // transform.NewWriter に渡すのが Encoder
    //
    // となる。
    // -------------------------------------------------------------

    // 書き出し対象データを用意
    var (
        original = []byte("こんにちわ世界")
        buf      = bytes.NewBuffer(original)
    )

    // EUC-JP の エンコーダー 経由で書き出し
    var (
        eucBuf    = new(bytes.Buffer)
        err       error
        encoder   *encoding.Encoder = japanese.EUCJP.NewEncoder()
        eucWriter *transform.Writer = transform.NewWriter(eucBuf, encoder)
    )

    if _, err = io.Copy(eucWriter, buf); err != nil {
        return err
    }

    // 結果確認
    output.Stdoutl("[original]", string(original))
    output.Stdoutl("[euc-jp  ]", eucBuf.String())

    return nil
}

CSVデータの読み込み

package csvop

import (
    "bytes"
    "encoding/csv"
    "fmt"
    "io"

    "github.com/devlights/gomy/output"
)

// Read は、csv.Reader を利用したCSVデータの読み込みのサンプルです.
func Read() error {
    var (
        buf = new(bytes.Buffer)
        err error
    )

    fmt.Fprintln(buf, "hello,world")
    fmt.Fprintln(buf, "world,hello")

    var (
        r   = csv.NewReader(buf)
        rec []string
    )

LOOP:
    for {
        if rec, err = r.Read(); err != nil {
            switch err {
            case io.EOF:
                break LOOP
            default:
                return err
            }
        }

        output.Stdoutf("[rec]", "%v\tlen=%d\n", rec, len(rec))
    }

    return nil
}

CSVデータの書き込み

package csvop

import (
    "bytes"
    "encoding/csv"
    "io"

    "github.com/devlights/gomy/output"
)

// Write は、csv.Writer を利用したCSVデータの書き込みのサンプルです.
func Write() error {
    var (
        buf = new(bytes.Buffer)
        err error
    )

    var (
        w       = csv.NewWriter(buf)
        records = [][]string{
            {"hello", "world"},
            {"world", "hello"},
        }
    )

    for _, rec := range records {
        if err = w.Write(rec); err != nil {
            return err
        }
    }
    w.Flush()

    var (
        data []byte
    )

    if data, err = io.ReadAll(buf); err != nil {
        return err
    }

    output.Stdoutf("[data]", "\n%v", string(data))

    return nil
}

TSVデータの読み込み

package tsvop

import (
    "bytes"
    "encoding/csv"
    "fmt"
    "io"

    "github.com/devlights/gomy/output"
)

// Read は、csv.Reader を利用したTSVデータの読み取りのサンプルです.
func Read() error {
    var (
        buf = new(bytes.Buffer)
        err error
    )

    fmt.Fprintln(buf, "hello\tworld")
    fmt.Fprintln(buf, "world\thello")

    var (
        r   = csv.NewReader(buf)
        rec []string
    )

    // TSV なので、区切り文字をタブに変更
    r.Comma = '\t'

LOOP:
    for {
        if rec, err = r.Read(); err != nil {
            switch err {
            case io.EOF:
                break LOOP
            default:
                return err
            }
        }

        output.Stdoutf("[rec]", "%v\tlen=%d\n", rec, len(rec))
    }

    return nil
}

TSVデータの書き込み

package tsvop

import (
    "bytes"
    "encoding/csv"
    "io"

    "github.com/devlights/gomy/output"
)

// Write は、csv.Writer を利用したTSVデータの書き込みのサンプルです.
func Write() error {
    var (
        buf = new(bytes.Buffer)
        err error
    )

    var (
        w       = csv.NewWriter(buf)
        records = [][]string{
            {"hello", "world"},
            {"world", "hello"},
        }
    )

    // TSV なので、区切り文字を変更
    w.Comma = '\t'

    for _, rec := range records {
        if err = w.Write(rec); err != nil {
            return err
        }
    }
    w.Flush()

    var (
        data []byte
    )

    if data, err = io.ReadAll(buf); err != nil {
        return err
    }

    output.Stdoutf("[data]", "\n%v", string(data))

    return nil
}

JSON (Marshal, シリアライズ)

package jsonop

import (
    "encoding/json"

    "github.com/devlights/gomy/output"
)

// MarshalNonIndent は、json.Marshal() を利用したサンプルです.
func MarshalNonIndent() error {
    type (
        V struct {
            notSerialize string
            Value1       string `json:"value"`
            Value2       string
        }
    )

    var (
        v = V{
            notSerialize: "not_serialize",
            Value1:       "value1",
            Value2:       "value2",
        }
    )

    var (
        buf []byte
        err error
    )

    if buf, err = json.Marshal(&v); err != nil {
        return err
    }

    output.Stdoutl("[marshal]", string(buf))

    return nil
}

インデント付きの場合は以下。

package jsonop

import (
    "encoding/json"

    "github.com/devlights/gomy/output"
)

// MarshalIndent は、json.MarshalIndent() を利用したサンプルです.
func MarshalIndent() error {
    type (
        V struct {
            Value1 string `json:"name"`
            Value2 string `json:"name2"`
        }
    )

    var (
        v = V{
            Value1: "hello",
            Value2: "world",
        }
    )

    var (
        buf []byte
        err error
    )

    if buf, err = json.MarshalIndent(&v, "", "  "); err != nil {
        return err
    }

    output.Stdoutl("[marshal]", string(buf))

    return nil
}

JSON (Unmarshal, デシリアライズ)

package jsonop

import (
    "bytes"
    "encoding/json"

    "github.com/devlights/gomy/output"
)

// Unmarshal は、json.Unmarshal() を利用した
func UnmarshalStruct() error {
    type (
        V struct {
            Value1 string `json:"name"`
            Value2 string `json:"value"`
        }
    )

    var (
        buf = bytes.NewBufferString(`
      {
          "name": "value1",
          "value": "value2"
      }
      `)
    )

    var (
        v   V
        err error
    )

    if err = json.Unmarshal(buf.Bytes(), &v); err != nil {
        return err
    }

    output.Stdoutl("[original]", buf.String())
    output.Stdoutf("[unmarshal]", "%#v\n", v)

    return nil
}

JSON (Marshal, 日付, RFC3339)

package jsonop

import (
    "encoding/json"
    "time"

    "github.com/devlights/gomy/output"
)

// MarshalDateRfc3339 は、RFC3339形式の日付を json.Marshal するサンプルです.
func MarshalDateRfc3339() error {
    type V struct {
        T time.Time `json:"t"`
    }

    var (
        v = V{time.Now()}
    )

    var (
        buf []byte
        err error
    )

    if buf, err = json.Marshal(&v); err != nil {
        return err
    }

    output.Stdoutl("[marshal]", string(buf))

    return nil
}

JSON (Unmarshal, 日付, RFC3339)

package jsonop

import (
    "bytes"
    "encoding/json"
    "fmt"
    "time"

    "github.com/devlights/gomy/output"
)

// UnmarshalDateRfc3339 は、RFC3339形式の日付文字列 を json.Unmarshal した場合のサンプルです.
func UnmarshalDateRfc3339() error {
    type V struct {
        T time.Time `json:"t"`
    }

    var (
        t   = time.Now().Format(time.RFC3339)
        buf = bytes.NewBufferString(fmt.Sprintf(`{"t": "%s"}`, t))
    )

    var (
        v   V
        err error
    )

    if err = json.Unmarshal(buf.Bytes(), &v); err != nil {
        return err
    }

    output.Stdoutl("[original]", buf.String())
    output.Stdoutl("[unmarshal]", v)

    return nil
}

JSON (Marshal, 日付, カスタム)

package types

import (
    "encoding/json"
    "fmt"
    _ "strconv"
    "time"
)

type (
    // YYYY/MM/DD 形式で json.Marshal/json.Unmarshal するために利用できる構造体です.
    YyyyMmDd struct {
        time.Time
    }
)

var (
    _ fmt.Stringer     = (*YyyyMmDd)(nil)
    _ json.Marshaler   = (*YyyyMmDd)(nil)
    _ json.Unmarshaler = (*YyyyMmDd)(nil)
)

func (me YyyyMmDd) String() string {
    return me.string()
}

func (me YyyyMmDd) MarshalJSON() ([]byte, error) {
    return []byte(me.jsonString()), nil
}

func (me *YyyyMmDd) UnmarshalJSON(b []byte) error {
    var (
        s   = string(b)
        err error
    )

    if s == "null" {
        return nil
    }

    // https://stackoverflow.com/questions/16846553/how-to-unmarshal-an-escaped-json-string
    // if s, err = strconv.Unquote(s); err != nil {
    //     return err
    // }

    var (
        t time.Time
    )

    // https://essential-go.programming-books.io/custom-json-marshaling-468765d144a34e87b913c7674e66c3a4#12ec5c24-ae51-4341-95ae-a5a0a81ed746
    if t, err = time.Parse(`"2006/01/02"`, s); err != nil {
        return err
    }

    me.Time = t

    return nil
}

func (me YyyyMmDd) string() string {
    return me.Time.Format("2006/01/02")
}

func (me YyyyMmDd) jsonString() string {
    return fmt.Sprintf(`"%s"`, me.string())
}
package jsonop

import (
    "encoding/json"
    "time"

    "github.com/devlights/gomy/output"
    "github.com/devlights/try-golang/examples/basic/jsonop/types"
)

// MarshalDateCustom は、json.Marshal にて 独自の日付形式 を利用するサンプルです.
func MarshalDateCustom() error {
    var (
        ct = types.YyyyMmDd{Time: time.Now()}
    )

    var (
        buf []byte
        err error
    )

    if buf, err = json.Marshal(&ct); err != nil {
        return err
    }

    output.Stdoutl("[original]", ct)
    output.Stdoutl("[marshal]", string(buf))

    return nil
}

JSON (Unmarshal, 日付, カスタム)

package types

import (
    "encoding/json"
    "fmt"
    _ "strconv"
    "time"
)

type (
    // YYYY/MM/DD 形式で json.Marshal/json.Unmarshal するために利用できる構造体です.
    YyyyMmDd struct {
        time.Time
    }
)

var (
    _ fmt.Stringer     = (*YyyyMmDd)(nil)
    _ json.Marshaler   = (*YyyyMmDd)(nil)
    _ json.Unmarshaler = (*YyyyMmDd)(nil)
)

func (me YyyyMmDd) String() string {
    return me.string()
}

func (me YyyyMmDd) MarshalJSON() ([]byte, error) {
    return []byte(me.jsonString()), nil
}

func (me *YyyyMmDd) UnmarshalJSON(b []byte) error {
    var (
        s   = string(b)
        err error
    )

    if s == "null" {
        return nil
    }

    // https://stackoverflow.com/questions/16846553/how-to-unmarshal-an-escaped-json-string
    // if s, err = strconv.Unquote(s); err != nil {
    //     return err
    // }

    var (
        t time.Time
    )

    // https://essential-go.programming-books.io/custom-json-marshaling-468765d144a34e87b913c7674e66c3a4#12ec5c24-ae51-4341-95ae-a5a0a81ed746
    if t, err = time.Parse(`"2006/01/02"`, s); err != nil {
        return err
    }

    me.Time = t

    return nil
}

func (me YyyyMmDd) string() string {
    return me.Time.Format("2006/01/02")
}

func (me YyyyMmDd) jsonString() string {
    return fmt.Sprintf(`"%s"`, me.string())
}
package jsonop

import (
    "bytes"
    "encoding/json"

    "github.com/devlights/gomy/output"
    "github.com/devlights/try-golang/examples/basic/jsonop/types"
)

// UnmarshalDateCustom は、独自の日付文字列 を json.Unmarshal した場合のサンプルです.
func UnmarshalDateCustom() error {
    var (
        buf = bytes.NewBufferString(`"2021/06/04"`)
    )

    var (
        v   types.YyyyMmDd
        err error
    )

    if err = json.Unmarshal(buf.Bytes(), &v); err != nil {
        return err
    }

    output.Stdoutl("[original]", buf.String())
    output.Stdoutl("[unmarshal]", v)

    return nil
}

JSON (Decoder)

package jsonop

import (
    "bytes"
    "encoding/json"
    "io"

    "github.com/devlights/gomy/output"
)

// Decoder は、json.NewDecoder を使ったサンプルです.
func Decoder() error {
    const (
        jsonStr = `
      { "id":100, "value": "golang"  }
      { "id":200, "value": "flutter" }
      `
    )

    type (
        Message struct {
            Id    int    `json:"id"`
            Value string `json:"value"`
        }
    )

    var (
        reader  = bytes.NewBufferString(jsonStr)
        decoder = json.NewDecoder(reader)
    )

LOOP:
    for {
        var (
            msg Message
            err error
        )

        if err = decoder.Decode(&msg); err != nil {
            switch err {
            case io.EOF:
                break LOOP
            default:
                return err
            }
        }

        output.Stdoutf("[msg]", "%v\n", msg)
    }

    return nil
}

JSON (Encoder)

package jsonop

import (
    "bytes"
    "encoding/json"

    "github.com/devlights/gomy/output"
)

// Encoder は、json.Encoder を使ったサンプルです.
func Encoder() error {
    type (
        Message struct {
            Id    int    `json:"id"`
            Value string `json:"value"`
        }
    )

    var (
        buf     = new(bytes.Buffer)
        encoder = json.NewEncoder(buf)
    )

    var (
        msgs = []*Message{
            {Id: 100, Value: "golang"},
            {Id: 200, Value: "flutter"},
        }
    )

    for _, msg := range msgs {
        var (
            err error
        )

        if err = encoder.Encode(msg); err != nil {
            return err
        }
    }

    output.Stdoutf("[encode]", "\n%v\n", buf.String())

    return nil
}

XML (Marshal, シリアライズ)

package types

import "encoding/xml"

type (
    Version struct {
        Major int `xml:"major,attr"`
        Minor int `xml:"minor,attr"`
    }

    Language struct {
        Name    string  `xml:"name,attr"`
        PrintFn string  `xml:"printfn"`
        Version Version `xml:"version"`
    }

    XmlData struct {
        XMLName   xml.Name   `xml:"data"`
        Languages []Language `xml:"languages"`
    }
)
package xmlop

import (
    "encoding/xml"

    "github.com/devlights/gomy/output"
    "github.com/devlights/try-golang/examples/basic/xmlop/types"
)

func Marshal() error {
    var (
        v = types.XmlData{
            Languages: []types.Language{
                {Name: "golang", PrintFn: "fmt.Println", Version: types.Version{Major: 1, Minor: 16}},
                {Name: "java", PrintFn: "System.out.println", Version: types.Version{Major: 16, Minor: 0}},
            },
        }
    )

    var (
        buf []byte
        err error
    )

    if buf, err = xml.Marshal(&v); err != nil {
        return err
    }

    output.Stdoutf("[marshal]", "\n%s\n", string(buf))

    return nil
}

インデントを付けたい場合は以下。

package xmlop

import (
    "encoding/xml"

    "github.com/devlights/gomy/output"
    "github.com/devlights/try-golang/examples/basic/xmlop/types"
)

// MarshalIndent は、xml.MarshalIndent() を使ったサンプルです.
func MarshalIndent() error {
    var (
        v = types.XmlData{
            Languages: []types.Language{
                {Name: "golang", PrintFn: "fmt.Println", Version: types.Version{Major: 1, Minor: 16}},
                {Name: "java", PrintFn: "System.out.println", Version: types.Version{Major: 16, Minor: 0}},
            },
        }
    )

    var (
        buf []byte
        err error
    )

    if buf, err = xml.MarshalIndent(&v, "", "    "); err != nil {
        return err
    }

    output.Stdoutf("[marshal]", "\n%s\n", string(buf))

    return nil
}

XML (Unmarshal, デシリアライズ)

package types

import "encoding/xml"

type (
    Version struct {
        Major int `xml:"major,attr"`
        Minor int `xml:"minor,attr"`
    }

    Language struct {
        Name    string  `xml:"name,attr"`
        PrintFn string  `xml:"printfn"`
        Version Version `xml:"version"`
    }

    XmlData struct {
        XMLName   xml.Name   `xml:"data"`
        Languages []Language `xml:"languages"`
    }
)
package xmlop

import (
    "bytes"
    "encoding/xml"

    "github.com/devlights/gomy/output"
    "github.com/devlights/try-golang/examples/basic/xmlop/types"
)

// Unmarshal は、xml.Unmarshal() を使ったサンプルです.
func Unmarshal() error {
    const (
        xmlStr = `
<data>
  <languages name="golang">
      <printfn>fmt.Println</printfn>
      <version major="1" minor="16"></version>
  </languages>
  <languages name="java">
      <printfn>System.out.println</printfn>
      <version major="16" minor="0"></version>
  </languages>
</data>`
    )

    var (
        buf = bytes.NewBufferString(xmlStr)
        v   types.XmlData
        err error
    )

    if err = xml.Unmarshal(buf.Bytes(), &v); err != nil {
        return err
    }

    output.Stdoutf("[unmarshal]", "%v\n", v)

    return nil
}

XML (Decoder)

package types

import "encoding/xml"

type (
    Version struct {
        Major int `xml:"major,attr"`
        Minor int `xml:"minor,attr"`
    }

    Language struct {
        Name    string  `xml:"name,attr"`
        PrintFn string  `xml:"printfn"`
        Version Version `xml:"version"`
    }

    XmlData struct {
        XMLName   xml.Name   `xml:"data"`
        Languages []Language `xml:"languages"`
    }
)
package xmlop

import (
    "bytes"
    "encoding/xml"

    "github.com/devlights/gomy/output"
    "github.com/devlights/try-golang/examples/basic/xmlop/types"
)

// Decoder は、xml.Decoder を使ったサンプルです.
func Decoder() error {
    const (
        xmlStr = `
<data>
  <languages name="golang">
      <printfn>fmt.Println</printfn>
      <version major="1" minor="16"></version>
  </languages>
  <languages name="java">
      <printfn>System.out.println</printfn>
      <version major="16" minor="0"></version>
  </languages>
</data>`
    )

    var (
        buf = bytes.NewBufferString(xmlStr)
        dec = xml.NewDecoder(buf)
    )

    var (
        v   types.XmlData
        err error
    )

    if err = dec.Decode(&v); err != nil {
        return err
    }

    output.Stdoutf("[decoder]", "%v\n", v)

    return nil
}

XML (Encoder)

package types

import "encoding/xml"

type (
    Version struct {
        Major int `xml:"major,attr"`
        Minor int `xml:"minor,attr"`
    }

    Language struct {
        Name    string  `xml:"name,attr"`
        PrintFn string  `xml:"printfn"`
        Version Version `xml:"version"`
    }

    XmlData struct {
        XMLName   xml.Name   `xml:"data"`
        Languages []Language `xml:"languages"`
    }
)
package xmlop

import (
    "bytes"
    "encoding/xml"

    "github.com/devlights/gomy/output"
    "github.com/devlights/try-golang/examples/basic/xmlop/types"
)

// Encoder は、xml.Encoder を使ったサンプルです.
func Encoder() error {
    var (
        v = types.XmlData{
            Languages: []types.Language{
                {Name: "golang", PrintFn: "fmt.Println", Version: types.Version{Major: 1, Minor: 16}},
                {Name: "java", PrintFn: "System.out.println", Version: types.Version{Major: 16, Minor: 0}},
            },
        }
    )

    var (
        buf = new(bytes.Buffer)
        enc = xml.NewEncoder(buf)
        err error
    )

    if err = enc.Encode(&v); err != nil {
        return err
    }

    output.Stdoutf("[encoder]", "\n%s\n", buf.String())

    return nil
}

YAML (Marshal, シリアライズ)

package types

type (
    Version struct {
        Major int `yaml:"major"`
        Minor int `yaml:"minor"`
    }

    Language struct {
        Name    string  `yaml:"name"`
        PrintFn string  `yaml:"printfn"`
        Version Version `yaml:"version"`
    }

    YamlData struct {
        Languages []Language `yaml:"languages"`
    }
)
package yamlop

import (
    "github.com/devlights/gomy/output"
    "github.com/devlights/try-golang/examples/basic/yamlop/types"
    "gopkg.in/yaml.v2"
)

// Marshal は、yaml.Marshal() を利用したサンプルです.
func Marshal() error {
    var (
        v = types.YamlData{
            Languages: []types.Language{
                {Name: "golang", PrintFn: "fmt.Println", Version: types.Version{Major: 1, Minor: 16}},
                {Name: "java", PrintFn: "System.out.println", Version: types.Version{Major: 16, Minor: 0}},
            },
        }
    )

    var (
        buf []byte
        err error
    )

    if buf, err = yaml.Marshal(&v); err != nil {
        return err
    }

    output.Stdoutf("[yaml]", "\n%s\n", string(buf))

    return nil
}

YAML (Unmarshal, デシリアライズ)

package types

type (
    Version struct {
        Major int `yaml:"major"`
        Minor int `yaml:"minor"`
    }

    Language struct {
        Name    string  `yaml:"name"`
        PrintFn string  `yaml:"printfn"`
        Version Version `yaml:"version"`
    }

    YamlData struct {
        Languages []Language `yaml:"languages"`
    }
)
package yamlop

import (
    "github.com/devlights/gomy/output"
    "github.com/devlights/try-golang/examples/basic/yamlop/types"
    "gopkg.in/yaml.v2"
)

func Unmarshal() error {
    const (
        yamlStr = `
languages:
- name: golang
  printfn: fmt.Println
  version:
    major: 1
    minor: 16
- name: java
  printfn: System.out.println
  version:
    major: 16
    minor: 0`
    )

    var (
        v   types.YamlData
        err error
    )

    if err = yaml.Unmarshal([]byte(yamlStr), &v); err != nil {
        return err
    }

    output.Stdoutf("[unmarshal]", "%v\n", v)

    return nil
}

YAML (Decoder)

package types

type (
    Version struct {
        Major int `yaml:"major"`
        Minor int `yaml:"minor"`
    }

    Language struct {
        Name    string  `yaml:"name"`
        PrintFn string  `yaml:"printfn"`
        Version Version `yaml:"version"`
    }

    YamlData struct {
        Languages []Language `yaml:"languages"`
    }
)
package yamlop

import (
    "bytes"

    "github.com/devlights/gomy/output"
    "github.com/devlights/try-golang/examples/basic/yamlop/types"
    "gopkg.in/yaml.v2"
)

// Decoder は、yaml.Decoder のサンプルです.
func Decoder() error {
    const (
        yamlStr = `
languages:
- name: golang
  printfn: fmt.Println
  version:
    major: 1
    minor: 16
- name: java
  printfn: System.out.println
  version:
    major: 16
    minor: 0`
    )

    var (
        buf = bytes.NewBufferString(yamlStr)
        dec = yaml.NewDecoder(buf)
    )

    var (
        v   types.YamlData
        err error
    )

    if err = dec.Decode(&v); err != nil {
        return err
    }

    output.Stdoutf("[decoder]", "%v\n", v)

    return nil
}

YAML (Encoder)

package types

type (
    Version struct {
        Major int `yaml:"major"`
        Minor int `yaml:"minor"`
    }

    Language struct {
        Name    string  `yaml:"name"`
        PrintFn string  `yaml:"printfn"`
        Version Version `yaml:"version"`
    }

    YamlData struct {
        Languages []Language `yaml:"languages"`
    }
)
package yamlop

import (
    "bytes"

    "github.com/devlights/gomy/output"
    "github.com/devlights/try-golang/examples/basic/yamlop/types"
    "gopkg.in/yaml.v2"
)

// Encoder は、yaml.Encoder についてのサンプルです.
func Encoder() error {
    var (
        v = types.YamlData{
            Languages: []types.Language{
                {Name: "golang", PrintFn: "fmt.Println", Version: types.Version{Major: 1, Minor: 16}},
                {Name: "java", PrintFn: "System.out.println", Version: types.Version{Major: 16, Minor: 0}},
            },
        }
    )

    var (
        buf = new(bytes.Buffer)
        enc = yaml.NewEncoder(buf)
        err error
    )

    if err = enc.Encode(&v); err != nil {
        return err
    }

    output.Stdoutf("[encoder]", "\n%s\n", buf.String())

    return nil
}

バイナリデータの読み込み

package binaryop

import (
    "bytes"
    "encoding/binary"

    "github.com/devlights/gomy/output"
)

// Read は、バイナリを読み込むサンプルです.
func Read() error {
    var (
        bin = []byte{
            0, 0, 0, 1,
            2, 0, 0, 0,

            0x68, 0x65, 0x6c, 0x6c, 0x6f,
            // h    e     l     l     o
            0x77, 0x6f, 0x72, 0x6c, 0x64,
            // w    o     r     l     d

        }
        buf = bytes.NewBuffer(bin)
    )

    var (
        i   int32
        err error
    )

    if err = binary.Read(buf, binary.BigEndian, &i); err != nil {
        return err
    }

    var (
        i2 int32
    )

    if err = binary.Read(buf, binary.LittleEndian, &i2); err != nil {
        return nil
    }

    var (
        hello = make([]byte, 5)
        world = make([]byte, 5)
    )

    if err = binary.Read(buf, binary.BigEndian, &hello); err != nil {
        return err
    }

    if _, err = buf.Read(world); err != nil {
        return err
    }

    output.Stdoutl("[bin   ]", bin)
    output.Stdoutl("[output]", i, i2, string(hello), string(world))

    return nil
}

実行すると以下の感じ。

$ make run
go get -d ./...
go run -race github.com/devlights/try-golang/cmd/trygolang -onetime -example ""
ENTER EXAMPLE NAME: binaryop_read
[Name] "binaryop_read"
[bin   ]             [0 0 0 1 2 0 0 0 104 101 108 108 111 119 111 114 108 100]
[output]             1 2 hello world


[Elapsed] 223.7µs

バイナリデータの書き込み

package binaryop

import (
    "bytes"
    "encoding/binary"

    "github.com/devlights/gomy/output"
)

// Write は、バイナリを書き込むサンプルです.
func Write() error {
    var (
        buf = new(bytes.Buffer)
        err error
    )

    if err = binary.Write(buf, binary.BigEndian, int32(1)); err != nil {
        return err
    }

    if err = binary.Write(buf, binary.LittleEndian, int32(2)); err != nil {
        return err
    }

    if err = binary.Write(buf, binary.BigEndian, []byte("helloworld")); err != nil {
        return err
    }

    output.Stdoutl("[output]", buf.Bytes())

    return nil
}

実行すると以下な感じ。

$ make run
go get -d ./...
go run -race github.com/devlights/try-golang/cmd/trygolang -onetime -example ""
ENTER EXAMPLE NAME: binaryop_write
[Name] "binaryop_write"
[output]             [0 0 0 1 2 0 0 0 104 101 108 108 111 119 111 114 108 100]


[Elapsed] 134.5µs

バイナリデータを構造体にマッピング

package binaryop

import (
    "bytes"
    "encoding/binary"

    "github.com/devlights/gomy/output"
)

// MapStruct は、構造体にバイナリデータをマッピングするサンプルです.
func MapStruct() error {
    type (
        binSt struct {
            I1 uint32
            I2 uint16
            I3 uint8
            I4 uint8
            S  [10]byte
        }
    )

    var (
        bin = []byte{
            0, 0, 0, 1,
            0, 2,
            3,
            0xFF,

            0x68, 0x65, 0x6c, 0x6c, 0x6f,
            // h    e     l     l     o
            0x77, 0x6f, 0x72, 0x6c, 0x64,
            // w    o     r     l     d

        }
        buf = bytes.NewBuffer(bin)
    )

    var (
        st  binSt
        err error
    )

    if err = binary.Read(buf, binary.BigEndian, &st); err != nil {
        return err
    }

    output.Stdoutl("[output]", st)
    output.Stdoutl("[output]", st.I1, st.I2, st.I3, st.I4, string(st.S[:]))

    return nil
}

実行すると以下な感じ。

$ make run
go get -d ./...
go run -race github.com/devlights/try-golang/cmd/trygolang -onetime -example ""
ENTER EXAMPLE NAME: binaryop_map
[Name] "binaryop_mapping"
[output]             {1 2 3 255 [104 101 108 108 111 119 111 114 108 100]}
[output]             1 2 3 255 helloworld


[Elapsed] 303µs

参考資料

pkg.go.dev

stackoverflow.com

stackoverflow.com


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com