はじめに
Golangでテスト用に一時ファイルを生成し、中身を書き込み、最後に中身を読み込みたい、となった際にはまったので備忘録として残す
結論からいうと、ファイル読み込み時には、ファイルの先頭から読み込まれるようにすると確実っぽい
環境
- go v1.13
一時ファイルの作成
os.Create
でファイル名指定してファイルを作成
一時ファイルなので、defer os.Remove(f.Name())
で処理完了後にファイル削除するようにする
※Goバージョン1.16以降だとos.CreateTemp
という一時ファイル生成用メソッドが用意されているっぽい
が、こちらの場合でも明示的に削除する処理は記載必要とのこと(何のためのTempメソッドなのだろうか…)
参考:https://pkg.go.dev/os#CreateTemp
f, err := os.Create("example.csv")
if err != nil {
panic(err)
}
defer os.Remove(f.Name())
ファイル書き込み
こちらは標準パッケージの公式Docを参考にした
ここではCSV形式っぽく記載したり、見やすくするために改行したりしている
参考:https://pkg.go.dev/os#File.Write
c := `id,name,age
1,Alice,99
2,Bob,12`
if _, err = f.Write([]byte(c)); err != nil {
panic(err)
}
ファイル読み込み
こちらは下記のQiita参考にした
読み込み方法も書き込み方法も複数パターン載っているので参考なるかと
参考:https://qiita.com/qt-luigi/items/2c13ad68e7d9f8f8c0f2
b := make([]byte, 10)
for {
c, err := f.Read(b)
if c == 0 {
break
}
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
line := string(b[:c])
fmt.Print(line)
}
一連のコード(失敗)
ここまでの情報をもとに一連のコードを記載してみるが、これでは何も出力されない悲しい結果になってしまった
package main
import (
"fmt"
"io"
"os"
)
func main() {
f, err := os.Create("example.csv")
if err != nil {
panic(err)
}
defer os.Remove(f.Name())
c := `id,name,age
1,Alice,99
2,Bob,12`
if _, err = f.Write([]byte(c)); err != nil {
panic(err)
}
b := make([]byte, 10)
for {
c, err := f.Read(b)
if c == 0 {
break
}
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
line := string(b[:c])
fmt.Print(line)
}
}
一連のコード(成功)
どうやらファイル書き込み後、Closeなどせずにすぐに読み込むとファイルの末尾から処理が始まるらしい
つまり、失敗したコードでは2,Bob,12
以降を読み取ろうとしていて、それ以降には何もないので何も出力されない結果になった
そのため、ファイルの中身全てを出力したい場合は、一度先頭に戻ってから読み取り処理始まるようにする必要がある
これを反映したコードが下記
package main
import (
"fmt"
"io"
"os"
)
func main() {
f, err := os.Create("example.csv")
if err != nil {
panic(err)
}
defer os.Remove(f.Name())
c := `id,name,age
1,Alice,99
2,Bob,12`
if _, err = f.Write([]byte(c)); err != nil {
panic(err)
}
// ここから追記
if _, err = f.Seek(0, 0); err != nil {
panic(err)
}
// ここまで追記
b := make([]byte, 10)
for {
c, err := f.Read(b)
if c == 0 {
break
}
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
line := string(b[:c])
fmt.Print(line)
}
}
さいごに
先頭に戻る必要があることには気づいた背景には、gocsvのサンプルコード眺めていてf.Seek(0, 0) //Go to the start of the file
のようなコードを見つけたことがある
コードリーディングって偉大
参考:https://github.com/gocarina/gocsv#easy-binding-in-go
また、今回のコードは下記のPlaygroundで試せるのでよければどうぞ