今川館

都内勤務の地味OLです

Goのファイル・ファイルライクオブジェクトもろもろ

ファイルの読み取り

Goでファイルを1行ずつ読み出したい場合はbufio.NewScannerを使うらしい。

そのNewScannerはio.Reader型の引数を取り、ファイルやファイルライクオブジェクトを渡す。

では、Goでファイルやファイルライクオブジェクトを扱う方法を調べてみた(以下3つ)。

Go Python3
1 strings.NewReader/bytes.NewReader io.StringIO
2 ioutil.TempFile tempfile.NamedTemporaryFile
3 os.Open ビルトインのopen関数

1. strings.NewReader/bytes.NewReaderを使う方法 (PythonのStringIOに相当)

文字列をファイルのように扱うファイルライクオブジェクトとして、Goではstrings.NewReaderを使うらしい。

[追記: 2016/12/6]

stringを反復する場合はstrings.NewReader, byteを反復する場合はbytes.NewReaderと、2種類用意されている。
テキストファイルの代わりに使うならstrings.NewReaderが妥当。

x := bufio.NewScanner(strings.NewReader(text))
// または
// x := bufio.NewScanner(bytes.NewReader([]byte(text)))

for x.Scan() {
	fmt.Println(x.Text())
}

2. ioutil.TempFileを使う方法 (PythonのNamedTemporaryFileに相当)

Goにも一時ファイルをパァーッっと作ってくれるライブラリがあった。ioutil.TempFileがこれに当たる。

// import "io/ioutil"
tmp, _ := ioutil.TempFile("", "_")
tmp.Write([]byte("| わたしは\n"))
tmp.Write([]byte("| い*がわ\n"))
tmp.Write([]byte("| よ**と"))
defer os.Remove(tmp.Name()) // PythonのNamedTemporaryFileと違い、ファイルの削除は自分でやる。

tmp.Seek(0, 0)
x = bufio.NewScanner(tmp)

for x.Scan() {
	fmt.Println(x.Text())
}

3. os.Openを使う方法 (Pythonのビルトインopen関数に相当)

普通にファイルを開くときは、os.Openを使う。

fp, _ := os.Open(tmp.Name())
defer fp.Close()
x = bufio.NewScanner(fp)
for x.Scan() {
	fmt.Println(x.Text())
}

まとめ

当然ながら、これらはすべてio.Reader型の変数に代入できる(以下)。

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"bufio"
	"bytes"
	"os"
)

func main(){
	var reader io.Reader
	var scanner *bufio.Scanner

	reader = bytes.NewReader([]byte("Hello, world."))
	scanner = bufio.NewScanner(reader)
	fmt.Println(scanner)

	tmp, _ := ioutil.TempFile("", "_")
	defer os.Remove(tmp.Name())
	tmp.Write([]byte("I have a pen."))
	tmp.Seek(0, 0)
	
	reader = tmp
	scanner = bufio.NewScanner(reader)
	fmt.Println(scanner)

	fp, _ := os.Open(tmp.Name())
	defer fp.Close()
	reader = fp
	scanner = bufio.NewScanner(reader)
	fmt.Println(scanner)
}