Goを書いているとio.writer
とio.reader
を扱うケースが頻繁に出てきますが、これはio
パッケージが多くの他のパッケージのインターフェースになっているからなのでio
パッケージを知っておくことで開発が楽になります。
参考書籍
本書はGo
のio.Writer
、io.Reader
からはじまりシステムの深いところまで丁寧に説明されているのでとてもおすすめです。io
パッケージから始まっているのも納得です。Webで無料で見れますし書籍版、PDF版で購入も可能です。
Web版
書籍版
Goならわかるシステムプログラミング
- 渋川 よしき,ごっちん
- ラムダノート
- 価格¥5,120(2022/05/07 13:34時点)
- 発売日2017/10/23
- 商品ランキング192,455位
io
パッケージのインターフェース一覧
io
パッケージのインターフェース一覧です。Go
のインターフェースの実装は、明示的にインターフェースを明示的にimplements
せず、インターフェースを満たしていたらimplements
していることになります。
- Reader(インターフェース)
- Writer(インターフェース)
- Seeker(インターフェース)
- Closer(インターフェース)
- ReadWriter(複合インターフェース)
- ReadCloser(複合インターフェース)
- WriteCloser(複合インターフェース)
- ReadSeeker(複合インターフェース)
- WriteSeeker(複合インターフェース)
- ReadWriteCloser(複合インターフェース)
- ReadWriteSeeker(複合インターフェース)
- ReaderFrom(インターフェース)
- WriterTo(インターフェース)
- ReaderAt(インターフェース)
- WriterAt(インターフェース)
- ByteReader(インターフェース)
- ByteScanner(インターフェース)
- ByteWriter(インターフェース)
- RuneScanner(インターフェース)
- StringWriter(インターフェース)
複合インターフェース表
Go
ではインターフェースにインターフェースを食わせることができ、io
パッケージで作られている複合インターフェースは以下となります。
インターフェース | io.Reader | io.Writer | io.Seeker | io.Closer |
---|---|---|---|---|
io.ReadWriter | ◯ | ◯ | ||
io.ReadSeeker | ◯ | ◯ | ||
io.ReadCloser | ◯ | ◯ | ||
io.WriteSeeker | ◯ | ◯ | ||
io.WriteCloser | ◯ | ◯ | ||
io.ReadWriteSeeker | ◯ | ◯ | ◯ | |
io.ReadWriteCloser | ◯ | ◯ | ◯ |
インターフェースを満たしている一覧を確認する方法
以下のコマンドを叩くと対象のインターフェースを満たしているものの一覧が確認できます。
$ GOPATH=/ godoc -http ":6060" -analysis type
## 実行後以下のURLにアクセスすると`io`パッケージが確認できる
http://localhost:6060/pkg/io/
ioパッケージのパブリックメソッドを叩いてみる
io
パッケージにはインターフェース以外に実装されている関数があるので叩いてみました。
WriteString
writerに文字列を書き込む
func WriteString() string {
file, _ := os.Create("testdata/src.txt")
_, _ = io.WriteString(file, "0123456789")
b, _ := ioutil.ReadFile("testdata/src.txt")
return string(b)
}
WriteStringのテスト
func TestWriteString(t *testing.T) {
str := WriteString()
if str != "0123456789" {
t.Errorf("WriteString Error %s", str)
}
}
ReadAtLeast
指定した値は最低限読み込みバッファー分すべて読み込む(パッケージ内部では使われていますが結構ニッチな機能)
func ReadAtLeast(b int, min int) (string, error) {
file, _ := os.Open("testdata/src.txt")
buf := make([]byte, b)
n, err := io.ReadAtLeast(file, buf, min)
return string(buf[:n]), err
}
ReadAtLeastのテスト
func TestReadAtLeast(t *testing.T) {
str, _ := ReadAtLeast(5, 2)
if str != "01234" {
t.Errorf("TestReadAtLeast Error %s", str)
}
}
確保するバッファーよりも最低限読み込むバッファーの方が大きい場合はshort buffer
エラーとなる
func TestReadAtLeast_shortBuffer(t *testing.T) {
_, err := ReadAtLeast(1, 2)
if err.Error() != "short buffer" {
t.Errorf("TestReadAtLeast Error %s", err.Error())
}
}
読み込む対象のバッファーより最低限読み込むバッファーの方が大きい場合はunexpected EOF
エラーがとなる
func TestReadAtLeast_unexpectedEOF(t *testing.T) {
_, err := ReadAtLeast(20, 15)
if err.Error() != "unexpected EOF" {
t.Errorf("TestReadAtLeast Error %s", err.Error())
}
}
ReadFull
指定したバッファー分すべて読み込む
func ReadFull() string {
file, _ := os.Open("testdata/src.txt")
buf := make([]byte, 5)
n, _ := io.ReadFull(file, buf)
return string(buf[:n])
}
ReadFullのテスト
func TestReadFull(t *testing.T) {
str := ReadFull()
if str != "01234" {
t.Errorf("TestReadFull Error %s", str)
}
}
CopyN
writerへ指定した値をコピーする
func CopyN() string {
srcFile, _ := os.Open("testdata/src.txt")
dstFile, _ := os.Create("testdata/dst.txt")
written, _ := io.CopyN(dstFile, srcFile, 5)
file, _ := os.Open("testdata/dst.txt")
buf := make([]byte, written)
n, _ := file.Read(buf)
return string(buf[:n])
}
CopyNのテスト
func TestCopyN(t *testing.T) {
str := CopyN()
if str != "01234" {
t.Errorf("TestCopyN Error %s", str)
}
}
Copy
readerをwriterにコピーする
func Copy() string {
srcFile, _ := os.Open("testdata/src.txt")
dstFile, _ := os.Create("testdata/dst.txt")
written, _ := io.Copy(dstFile, srcFile)
buf := make([]byte, written)
file, _ := os.Open("testdata/dst.txt")
n, _ := file.Read(buf)
return string(buf[:n])
}
Copyのテスト
func TestCopy(t *testing.T) {
str := Copy()
if str != "0123456789" {
t.Errorf("TestCopy Error %s", str)
}
}
CopyBuffer
readerをwriterに指定したバッファー分コピーする
func CopyBuffer() string {
srcFile, _ := os.Open("testdata/src.txt")
dstFile, _ := os.Create("testdata/dst.txt")
written, _ := io.CopyBuffer(dstFile, srcFile, make([]byte, 5))
buf := make([]byte, written)
file, _ := os.Open("testdata/dst.txt")
n, _ := file.Read(buf)
return string(buf[:n])
}
CopyBufferのテスト
func TestCopyBuffer(t *testing.T) {
str := CopyBuffer()
if str != "0123456789" {
t.Errorf("TestCopyBuffer Error %s", str)
}
}
LimitReaderRead
値を制限したreaderを取得する
func LimitReaderRead() string {
srcFile, _ := os.Open("testdata/src.txt")
limitedReader := io.LimitedReader{srcFile, 5}
buf := make([]byte, 3)
n, _ := limitedReader.Read(buf)
return string(buf[:n])
}
値を制限したreaderを取得し読み込む
func LimitReaderRead2() string {
srcFile, _ := os.Open("testdata/src.txt")
limitedReader := io.LimitedReader{srcFile, 5}
buf := make([]byte, 8)
n, _ := limitedReader.Read(buf)
return string(buf[:n])
}
LimitReaderReadのテスト
func TestLimitReaderRead(t *testing.T) {
str := LimitReaderRead()
if str != "012" {
t.Errorf("TestLimitReaderRead Error %s" , str)
}
}
func TestLimitReaderRead2(t *testing.T) {
str := LimitReaderRead2()
if str != "01234" {
t.Errorf("TestLimitReaderRead Error %s" , str)
}
}
SectionReaderRead
指定した値でセクション分けしたreaderを取得し読み込む
func SectionReaderRead() string {
file, _ := os.Open("testdata/src.txt")
sectionReader := io.NewSectionReader(file, 3, 5)
buf := make([]byte, 3)
n, _ := sectionReader.Read(buf)
return string(buf[:n])
}
SectionReaderReadのテスト
func TestSectionReaderRead(t *testing.T) {
str := SectionReaderRead()
if str != "345" {
t.Errorf("TestSectionReaderRead Error %s", str)
}
}
SectionReaderSeek
セクションリーダーの読み込み位置を変更する
func SectionReaderSeek() string {
file, _ := os.Open("testdata/src.txt")
sectionReader := io.NewSectionReader(file, 5, 10)
buf := make([]byte, 3)
n, _ := sectionReader.Read(buf)
_, _ = sectionReader.Seek(0, 0)
return string(buf[:n])
}
SectionReaderSeekのテスト
func TestSectionReaderSeek(t *testing.T) {
str := SectionReaderSeek()
if str != "567" {
t.Errorf("TestSectionReaderSeek Error %s", str)
}
}
SectionReaderReadAt
セクションリーダーを指定した位置から読み込む
func SectionReaderReadAt() string {
file, _ := os.Open("testdata/src.txt")
sectionReader := io.NewSectionReader(file, 5, 10)
buf := make([]byte, 3)
n, _ := sectionReader.ReadAt(buf, 2)
return string(buf[:n])
}
SectionReaderReadAtのテスト
func TestSectionReaderReadAt(t *testing.T) {
str := SectionReaderReadAt()
if str != "789" {
t.Errorf("TestSectionReaderReadAt Error %s", str)
}
}
SectionReaderSize
セクションリーダーのサイズを取得する
func SectionReaderSize() int64 {
file, _ := os.Open("testdata/src.txt")
sectionReader := io.NewSectionReader(file, 5, 9)
return sectionReader.Size()
}
SectionReaderSizeのテスト
func TestSectionReaderSize(t *testing.T) {
size := SectionReaderSize()
if size != 9 {
t.Errorf("TestSectionReaderSize Error %d", size)
}
}
TeeReaderRead
readerとwriterを渡しておいて読み込んだら同時にwriterにも書き込む
func TeeReaderRead() string {
srcFile, _ := os.Open("testdata/src.txt")
dstFile, _ := os.Create("testdata/dst.txt")
teeReader := io.TeeReader(srcFile, dstFile)
buf := make([]byte, 3)
_, _ = teeReader.Read(buf)
dstFile2, _ := os.Open("testdata/dst.txt")
n, _ := dstFile2.Read(buf)
return string(buf[:n])
}
TeeReaderReadのテスト
func TestTeeReaderRead(t *testing.T) {
str := TeeReaderRead()
if str != "012" {
t.Errorf("TestTeeReaderRead Error %s", str)
}
}
※ 他にもMultiWriter
とPipe
もありますが省略してます
ソースコード
本記事のソースコード
taisa831/sandbox-go
You can’t perform that action at this time. You signed in with another tab or window. You signed out in another tab or window. Reload to refresh your session. Reload to refresh your session.