Goでのファイルの読み込みを調べた

公開:2021/02/24
更新:2021/02/25
3 min読了の目安(約2900字TECH技術記事

はじめに

Goでファイルの読み込みには以下のような選択肢があります。

  • osパッケージを使用して、バイト単位で読み込む。
  • bufioパッケージを使用して、行単位で読み込む。
  • io/ioutilパッケージを使用して、一括で読み込む。(Go 1.16では非推奨)
  • osパッケージを使用して、一括で読み込む。(Go 1.16で使用できる)

これらの違いを自分なりにまとめ、備忘録として残します。

この記事が他の人の参考になれば幸いです。
また、この記事の内容に間違った記載がありましたら、指摘していただけるとありがたいです。

以下の例で読み込むファイル名はexample.txtであり、
内容は以下のようになっています。

example.txt
example.txt

123456789
aiueo
あいうえお

環境

名前 バージョン
macOS Big Sur 11.2.1
Go 1.15.8

osパッケージでバイト単位で読み込む

osパッケージのReadメソッドを使用して、バイト単位で読み込みます。

package main

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

func main() {
	contents := ""
	f, err := os.Open("tmp/example.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	b := make([]byte, 12)
	for {
		n, err := f.Read(b)
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		contents += string(b[:n])
	}
	fmt.Println(contents)
}

出力

example.txt

123456789
aiueo
あいうえお

Open関数で読み込み専用でファイルを開きます。
重要なのはb := make([]byte, 12)のコードであり、要素数12のバイトのスライスを宣言しています。
このバイトのにf.Readメソッドで読み込んだ内容が書き込まれます。
つまり、この例の場合は、ファイルを12バイトずつ読み込んでいます。そしてファイルの最後まで読み込むと停止します。
f.Read関数の戻り値nはバイトのスライスに新しく書き込んだバイト数です。
なお、一度に読み込むバイト数を大きくすれば、一括で読み込むこともできます。

bufioパッケージで行単位で読み込む

bufioパッケージのTextメソッドを使用して、行単位で読み込みます。

package main

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

func main() {
	contents := ""
	f, err := os.Open("example.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		contents += scanner.Text()
		fmt.Println(contents)
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
	fmt.Println("RESULT: ", contents)
}

出力

example.txt
example.txt
example.txt123456789
example.txt123456789aiueo
example.txt123456789aiueoあいうえお
RESULT:  example.txt123456789aiueoあいうえお

ファイルをOpen関数で開き、構造体Fileのポインタを取得します。
NewScanner関数で新しい構造体Scannerのポインタを作成し、取得します。
Scanメソッドは、Textメソッドで文字列を取得するために、次のトークンまで読み込めるかをチェックします。読み込めない場合にはfalseを返します。
Textメソッドは、スキャンしたところまで読み込み、文字列を戻り値として返します。

bufioパッケージには他にも色々な区切りで読み込めるメソッドがたくさんあるので気になったら調べて見ると良いです。

io/ioutilパッケージで一括で読み込む

io/ioutilパッケージのReadFile関数を用いて、一括で読み込みます。

package main

import (
	"fmt"
	"io/ioutil"
	"log"
)

func main() {
	b, err := ioutil.ReadFile("example.txt")
	if err != nil {
		log.Fatal(err)
	}
	contents := string(b)
	fmt.Println(contents)
}

出力

example.txt

123456789
aiueo
あいうえお

ファイルパスを明示的に開く必要がなく、一番簡潔に記述することができます。
ReadFile関数はファイル名を引数に取り、ファイルの中身をバイトのスライスで返します。

(Go 1.16より)osパッケージで一括で読み込む

上のソースコードでioutilosに変えるだけです。
Go 1.16ではio/ioutilパッケージが非推奨になり、その関数はosパッケージもしくはioパッケージで利用できます。

参考