Goならわかるシステムプログラミング:第2章

第2章。

  • ファイルディスクリプタ・・・OSがカーネルのレイヤーで用意している抽象化の仕組み limits.confでいじるやつ。 0が標準入力 1が標準出力 2が標準エラー出力

  • 構造体が持つべきメソッドを表現するのがインタフェース

package main

import "fmt"

type Talker interface {
    Talk()
}

type Greeter struct {
    name string
}

func (g Greeter) Talk() {
    fmt.Printf("Hello, my name is %s\n", g.name)
}

func main() {
    var talker Talker

    talker = &Greeter{"wozozo"}
    talker.Talk()
}
  • ファイル出力の例
package main

import (
    "os"
)

func main() {
    file, err := os.Create("test.txt")
    if err != nil {
        panic(err)
    }
    file.Write([]byte("os.File example\n"))
    file.Close()
}
  • 画面出力
package main

import "os"

func main() {
    os.Stdout.Write([]byte("os.Stdout example\n"))
}
  • バッファ
package main

import (
    "bytes"
    "fmt"
)

func main() {
    var buffer bytes.Buffer
    buffer.Write([]byte("bytes.Buffer example\n"))
        //buffer.WriteString("bytes.Buffer example\n") これでもOK
    fmt.Println(buffer.String())
}
  • インターネットアクセス
package main

import (
    "net"
    "io"
    "os"
)

func main() {
    conn, err := net.Dial("tcp", "ascii.jp:80")
    if err != nil {
        panic(err)
    }
    io.WriteString(conn, "GET / HTTP1.0\r\nHost: ascii.jp\r\n\r\n")
    io.Copy(os.Stdout, conn)
}
  • httpパッケージを使う方法
package main

import (
    "net"
    "io"
    "os"
    "net/http"
)

func main() {
    conn, err := net.Dial("tcp", "ascii.jp:80")
    if err != nil {
        panic(err)
    }
    req, err := http.NewRequest("GET", "http://ascii.jp", nil)
    if err != nil {
        panic(err)
    }
    req.Write(conn)
    io.Copy(os.Stdout, conn)
}
  • ウェブサーバーからブラウザに対して書き込む
package main

import (
    "net/http"
    "io"
)

func handler(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "http.ResponseWriter sample")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}
  • デコレータ・・・io.Writerを受け取り、書き込まれtデータを加工して別のio.Writerに書き出す 下記の例は1つのWriteでファイルと標準出力両方に書き込んでいる
package main

import (
    "os"
    "io"
)

func main() {
    file, err := os.Create("multiwriter.txt")
    if err != nil {
        panic(err)
    }
    writer := io.MultiWriter(file, os.Stdout)
    io.WriteString(writer, "io.MultiWriter example\n")
}

*gzip圧縮してos.Fileに中継する

package main

import (
    "os"
    "compress/gzip"
    "io"
)

func main() {
    file, err := os.Create("test.txt.gz")
    if err != nil {
        panic(err)
    }
    writer := gzip.NewWriter(file)
    writer.Header.Name = "test.txt"
    io.WriteString(writer, "gzip.Writer example")
    writer.Close()
}
  • バッファをためておいてFlush()で書き込む
package main

import (
    "bufio"
    "os"
)

func main() {
    buffer := bufio.NewWriter(os.Stdout)
    buffer.WriteString("bufio.Writer ")
    buffer.Flush()
    buffer.WriteString("example\n")
    buffer.Flush()
}
  • Fprintf()
package main

import (
    "fmt"
    "os"
    "time"
)

func main() {
    fmt.Fprintf(os.Stdout, "Write with os.Stdout at %v", time.Now())
}
Write with os.Stdout at 2018-07-05 23:07:33.224526589 +0900 JST m=+0.000422627
  • JSONを整形して標準出力に出す
package main

import (
    "encoding/json"
    "os"
)

func main() {
    encoder := json.NewEncoder(os.Stdout)
    encoder.SetIndent("", "   ")
    encoder.Encode(map[string]string {
        "example": "encoding/json",
        "hello": "world",
    })
}

問題

Q2.1: ファイルに対するフォーマット出力

package main

import (
    "os"
    "fmt"
)

func main() {
    file, err := os.Create("test.txt")
    if err != nil {
        panic(err)
    }
    d := 1
    s := "s"
    f := 0.2
    fmt.Fprintf(file, "%d, %s, %f", d, s, f)
    file.Close()
}

Q2.2: CSV出力

package main

import (
    "os"
    "encoding/csv"
)

func main() {
    file, err := os.Create("test.csv")
    if err != nil {
        panic(err)
    }
    writer := csv.NewWriter(file)
    writer.Write([]string{"apple", "orange", "lemon"})
    writer.Write([]string{"red", "orange", "yellow"})
    writer.Flush()
    file.Close()
}

Q2.3 gzipされたJSON出力をしながら、標準出力にログを出力

package main

import (
    "net/http"
    "encoding/json"
    "io"
    "compress/gzip"
    "os"
)

func handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Encoding", "gzip")
    w.Header().Set("Content-Type", "application/json")
    source := map[string]string {
        "Hello": "World",
    }

    gzipWriter := gzip.NewWriter(w)
    encoder := json.NewEncoder(io.MultiWriter(gzipWriter, os.Stdout))
    encoder.SetIndent("", "   ")
    encoder.Encode(source)
    gzipWriter.Close()
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}