Q3.1 ファイルのコピー
package main import ( "io" "os" ) func main() { file, err := os.Open("old.txt") if err != nil { panic(err) } defer file.Close() newFile, err := os.Create("new.txt") if err != nil { panic(err) } defer newFile.Close() io.Copy(newFile, file) }
テスト
package main import ( "bytes" "io/ioutil" "os" "testing" ) func exists(filename string)bool { _, err := os.Stat(filename) return err == nil } func TestMainSuccess(t *testing.T){ if exists("new.txt") { os.Remove("new.txt") } main() oldFile, err := os.Open("old.txt") if err != nil { t.Fatalf("failed to open old file: %#v", err) } newFile, err := os.Open("new.txt") if err != nil { t.Fatalf("failed to open new file: %#v", err) } oldBuffer, err := ioutil.ReadAll(oldFile) if err != nil { t.Fatalf("failed to read content: %#v", err) } newBuffer, err := ioutil.ReadAll(newFile) if err != nil { t.Fatalf("failed to read content: %#v", err) } if bytes.Compare(oldBuffer, newBuffer) != 0 { t.Fatalf("each contents of files are different.") } os.Remove("new.txt") }
テストはもうちょっとスッキリかける気がする。
Q3.2 テスト用の適切なサイズのファイルを生成
package main import ( "crypto/rand" "io" "os" ) func main() { file, err := os.Create("samplefile") if err != nil { panic(err) } defer file.Close() r := io.LimitedReader{R: rand.Reader, N: 1024} io.Copy(file, &r) }
「io.Copy
を使わないように」と問題には書いてあったが、LimitReaderを使って読み出しているので問題ない?
テスト
package main import ( "os" "testing" ) func TestMainSuccess(t *testing.T) { main() info, err := os.Stat("samplefile") if err != nil { t.Fatalf("could not open file.") } if info.Size() != 1024 { t.Fatal("size was not 1024 byte.") } defer os.Remove("samplefile") }
ファイルサイズはos.Stat()
で取れる。
Q3.4 zipファイルをウェブサーバーからダウンロード
package main import ( "io" "net/http" "os" ) func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8081", nil) } func handler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/zip") w.Header().Set("Content-Disposition", "attachment; filename=ascii_sample.zip") file, err := os.Open("./chap3/test.zip") if err != nil { panic(err) } defer file.Close() io.Copy(w, file) }
Q3.5 CopyN
package main import ( "io" "os" "strings" ) func main() { r := strings.NewReader("strings reader example") CopyN(os.Stdout, r, 5) } func CopyN(dst io.Writer, src io.Reader, n int64) (written int64, err error) { written, err = io.Copy(dst, io.LimitReader(src, n)) if written == n { return n, nil } if written < n && err == nil { // src stopped early; must have been EOF. err = io.EOF } return }
実装はこちらのコピー。 https://golang.org/src/io/io.go?s=11939:12009#L329
Q3.6 ストリーム総集編
package main import ( "io" "os" "strings" ) var ( computer = strings.NewReader("COMPUTER") system = strings.NewReader("SYSTEM") programming = strings.NewReader("PROGRAMMING") ) func main() { var stream io.Reader charA := io.NewSectionReader(programming, 5, 1) charS := io.NewSectionReader(system, 0, 1) charC := io.NewSectionReader(computer, 0, 1) charI, pw := io.Pipe() w := io.MultiWriter(pw, pw) go func() { io.Copy(w, io.NewSectionReader(programming, 8, 1)) pw.Close() }() stream = io.MultiReader(charA, charS, charC, charI) io.Copy(os.Stdout, stream) }
同じwriterに2回書けるということに気づかず、時間を使ってしまった。 答えは下記のリンクにある(上記のものとは少し違う)
ASCII.jp:低レベルアクセスへの入り口(3):io.Reader後編 (2/2)|Goならわかるシステムプログラミング