見出し画像

Golang_bytes.Bufferの概要 #476

Go言語の標準ライブラリには、バッファを扱うための便利なパッケージbytesが含まれています。その中でも特に重要なのがbytes.Bufferです。業務で扱う場面があったので内容をまとめてみます。

bytes.Bufferとは

bytes.Bufferは、バイトスライスを効率的に扱うためのバッファです。これにより、バイトデータを追加、読み取り、変換などの操作を容易に行うことができます。以下のような特性を持ちます。

  • 可変長のバイトスライスを内部に保持

  • データの追加、読み取り、操作が簡単にできる

  • io.Writerやio.Readerなどのインターフェースを実装している

そもそもバイトデータとは

バイトを扱う、と言ってもピンと来にくいかもしれません。バイトデータは8ビットのデータ単位であり、数値、文字、バイナリデータなどを表現できます。Go言語では、byteはuint8型のエイリアスで、0から255までの整数値を表します。

バイトスライスとは

バイトスライスはその名の通りbyte型のスライスであり、複数のバイトデータを格納するために使用されます。文字列、ファイルデータ、ネットワークパケットなど、さまざまなバイナリデータを扱う際に使用されます。

改めて、バイトバッファとは

バイトバッファは、bytesパッケージに含まれるBuffer型を使用し、可変長のバイトデータを効率的に扱うためのデータ構造です。バイトスライスの管理を内部で行い、便利なメソッドを提供します。また、bytes.Bufferはio.Writerおよびio.Readerインターフェースを実装しているため、エンコードやデコードの際に広く利用されます

代表的なメソッド

以下の様なメソッドを提供しています。

  • Write(p []byte) (n int, err error): バッファにバイトスライスを書き込み

  • WriteString(s string) (n int, err error): バッファに文字列を書き込み

  • Read(p []byte) (n int, err error): バッファからバイトスライスに読み取り

  • ReadByte() (byte, error): バッファから単一のバイトを読み取り

  • String() string: バッファの内容を文字列として返す

  • Bytes() []byte: バッファの内容をバイトスライスとして返す

  • Len() int: バッファの現在の長さを返す

  • Reset(): バッファをリセットし、空の状態にする

  • Grow(n int): バッファの容量をnバイト増やす

つまり、バイトスライスをバイトバッファに入れておくと色々便利に扱える、というイメージですね。

バイトスライスをバイトバッファに保存

バイトスライスとバイトバッファについて整理できたところで、具体的な使い方を見ていきます。まずは一番基本的な書き方です。

package main

import (
	"bytes"
	"fmt"
)

func main() {
	// 空のバッファを作成
	var buf bytes.Buffer

	// バイトスライスからバッファに書き込む
	data := []byte("Hello, World!")
	buf.Write(data)

	// バッファの内容を表示
	fmt.Println(buf.String())
}

ただし、上記はバッファを値型で扱っています。バイトデータを扱う場面ではデータが大きくなることが想定されるので、以下の様にポインタ型で扱い、余計なメモリを消費しないようにした方が良いです。

package main

import (
	"bytes"
	"fmt"
)

func main() {
	// 空のバッファをポインタ型で作成
	var buf *bytes.Buffer

	// バイトスライスからバッファを作成
	data := []byte("Hello, World!")
	buf = bytes.NewBuffer(data)

	// バッファの内容を表示
	fmt.Println(buf.String())
}

余談ですが、Go言語では文字列(string)はバイトのシーケンスとして内部的に表現されており、つまり、文字列は単なるバイトの集まりです。そのため文字列はバイトスライス([]byte)に直接変換できます。


文字列をバイトスライスにエンコードしてバイトバッファに保存

文字列は直接バイトスライスに変換できますが、NewBufferString()を使えばそれも省略できます。

package main

import (
	"bytes"
	"fmt"
)

func main() {
	// 空のバッファをポインタ型で作成
	var buf *bytes.Buffer

	// 文字列からバッファを作成
	str := "Hello, Go!"
	buf = bytes.NewBufferString(str)

	fmt.Println("buf:", buf.String())
}


構造体をバイトスライスにエンコードしてバイトバッファに保存

構造体をエンコードするにはencoding/jsonパッケージが使えます。実際に開発する際はこのパターンが多いかもしれません。

package main

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

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

func main() {
	// エンコードするデータ
	person := Person{Name: "Alice", Age: 30}

	// bytes.Bufferを作成
	buf := bytes.NewBuffer([]byte{})

	// json.NewEncoderにbytes.Bufferを渡してエンコード
	enc := json.NewEncoder(buf)
	err := enc.Encode(person)
	if err != nil {
		fmt.Println("json.Encode failed:", err)
		return
	}

	// エンコードされた[]byteを取得
	jsonData := buf.Bytes()

	fmt.Println("Encoded JSON data:", string(jsonData))
}


ちなみにhttp.ResponseWriterのWrite()メソッドにバイトスライスを渡すと、それがレスポンスボディに書き込まれます。レスポンスボディに投入するデータに対してbytes.Bufferを使用する、というパターンは多そうなので、抑えておきたいですね。

ここまでお読みいただきありがとうございました!!

参考



いいなと思ったら応援しよう!