技術めも

golang memo

パッケージ
http://golang.jp/pkg

JSON

package main

import (
    "fmt"
    "encoding/json"
)

type Person struct {
    ID int         `json:"id"`
    Name string     `json:"name"`
    Email string     `json:"-"`
    Age int         `json:"age"`
    Adress string   `json:"adress,omitempty"`
    memo string
}

func main() {
    // 構造体 to JSON
    person := &Person{
        1,
        "Gopher",
        "tatenosystem@gmail.com",
        5,
        "",
        "golang",
    }
    b, _ := json.Marshal(person)
    fmt.Println(string(b)) // []byte -> string

    // JSON to 構造体
    var person2 Person
    b2 := []byte(`{"id":1, "name":"Gopher", "age":5}`)
    json.Unmarshal(b2, &person2)
    fmt.Println(person2)
}

ポインタ

package main

import (
    "fmt"
)

func callByValue(i int) {
    i = 20;
}

// p *int:番地  ->  *pi  実体
func callByRef(pi *int) {
    *pi = 30;
}

func main() {
    i := 10
    callByValue(i)
    fmt.Println(i)
    callByRef(&i) // i 実体 -> &i 番地
    fmt.Println(i)
}

// ポインタ  * で実体にアクセス
// 実体     & で番地に変換

ファイル

package main

import (
    "fmt"
    "log"
    "os"
)

func writeTest() {
    file, err := os.Create("./f.txt");
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    msg := []byte("Hello Word !\n")
    _, err = file.Write(msg) // 書き込まれたバイト数, err

//    _, err = file.WriteString("Hello tatenosystem :)\n")

//    _, err = fmt.Fprintf(file, "Hello (^^)/\n")

    if err != nil {
        log.Fatal(err)
    }
}

func readTest() {
    file, err := os.Open("./f.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()
    
    // スライスの用意
    msg := make([]byte, 12)

    _, err = file.Read(msg) // スライスの上限までしか取得できない
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(string(msg)) // []byte -> string
}

func main() {
    writeTest()
    readTest()
}

JSONファイル読み書き

package main

import (
    "fmt"
    "encoding/json"
    "log"
    "os"
)

type Person struct {
    ID int         `json:"id"`
    Name string     `json:"name"`
    Email string     `json:"-"`
    Age int         `json:"age"`
    Adress string   `json:"adress,omitempty"`
    memo string
}

func encodeTest() {
    // 構造体 to JSON
    person := &Person{
        1,
        "Gopher",
        "tatenosystem@gmail.com",
        5,
        "",
        "golang",
    }
    
    file, err := os.Create("./p.json")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    // エンコーダ取得
    encode := json.NewEncoder(file)
    // JSONエンコードしたデータの書き込み
    err = encode.Encode(person)
    if err != nil {
        log.Fatal(err)
    }
}

func decodeTest() {
    file, err := os.Open("./p.json")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()
    
    // データを書き込む構造体
    var person Person

    // デコーダ取得
    decoder := json.NewDecoder(file)
    // JSONデコードしたデータの読み込み
    err = decoder.Decode(&person)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println(person)
}

func main() {
    encodeTest()
    decodeTest()
}

io/ioutil パッケージ

package main

import (
    "fmt"
    "log"
    _ "os"
    "io/ioutil"
    "reflect"
)

func writeTest() {
    msg := []byte("HogeFuga Hello :) !!!\n")
    err := ioutil.WriteFile("./f.txt", msg, 0666)
    if err != nil {
        log.Fatal(err)
    }
}

func readTest() {
    // file, _ := os.Open("./f.txt")
    // msg, err := ioutil.ReadAll(file)    // サイズを気にしなくていい

    msg, err := ioutil.ReadFile("./f.txt")    // サイズを気にしなくていい

    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(reflect.TypeOf(msg)) // []uint8
    fmt.Println(string(msg)) // []byte -> string
}

func main() {
    readTest()
    writeTest()
}

net/http パッケージ

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi %s !", r.URL.Path[1:])
}

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

POST で JSON を受け取ってファイル保存

curl http://localhost:8080/person -d '{"id":1, "name":"tatenosystem"}'
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
)

type Person struct {
    ID   int   `json:id`
    Name string   `json:name`
}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello %s !", r.URL.Path[1:])
}

func PersionHandler(w http.ResponseWriter, r *http.Request) {
    defer r.Body.Close() // 処理の最後にBodyを閉じる
    
    if r.Method == "POST" {
        fmt.Println("POST")
        var person Person
        decoder := json.NewDecoder(r.Body)
        err := decoder.Decode(&person) // errが初めて出てくる
        if err != nil {
            log.Fatal(err)
        }

        // ファイル保存 {id}.txt
        filename := fmt.Sprintf("%d.txt", person.ID)
        file, err := os.Create(filename)
        if err != nil {
            log.Fatal(err)
        }
        defer file.Close()
        // ファイルにNameを書き込む
        _, err = file.WriteString(person.Name)
        if err != nil {
            log.Fatal(err)
        }
        
        // レスポンスとしてステータスコード201
        w.WriteHeader(http.StatusCreated)
        fmt.Fprintf(w, "%s OK!", person.Name)
    }
}

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

html/template パッケージ

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "io/ioutil"
    "os"
    "html/template"
    "strconv"
)

type Person struct {
    ID   int   `json:id`
    Name string   `json:name`
}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello %s !", r.URL.Path[1:])
}

var t = template.Must(template.ParseFiles("index.html"))

func PersionHandler(w http.ResponseWriter, r *http.Request) {
    defer r.Body.Close() // 処理の最後にBodyを閉じる
    
//    t, err := template.ParseFiles("index.html")
//    if err != nil {
//        log.Fatal(err)
//    }

    if r.Method == "POST" {
        fmt.Println("POST")
        var person Person
        decoder := json.NewDecoder(r.Body)
        err := decoder.Decode(&person) // errが初めて出てくる
        if err != nil {
            log.Fatal(err)
        }

        // ファイル保存 {id}.txt
        filename := fmt.Sprintf("%d.txt", person.ID)
        file, err := os.Create(filename)
        if err != nil {
            log.Fatal(err)
        }
        defer file.Close()
        // ファイルにNameを書き込む
        _, err = file.WriteString(person.Name)
        if err != nil {
            log.Fatal(err)
        }
        
        // レスポンスとしてステータスコード201
        w.WriteHeader(http.StatusCreated)
        fmt.Fprintf(w, "%s OK!", person.Name)
    } else if r.Method == "GET" {
        // パラメータ取得
        id, err := strconv.Atoi(r.URL.Query().Get("id"))
        if err != nil {
            fmt.Println("ERROR strconv")
            log.Fatal(err)
        }
        filename := fmt.Sprintf("%d.txt", id)
        b, err := ioutil.ReadFile(filename)
        if err != nil {
            log.Fatal(err)
        }
        // person作成
        person := Person{
            ID: id,
            Name: string(b),
        }
        // レスポンス
        t.Execute(w, person)
    }
}

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

index.html
Jinja2 ライクな記述

<html>
<head>
<title>person</title>
</head>
<body>
<h1>{{ .ID }} : {{ .Name }}</h1>
</body>
</html>

ゴールーチン

直列処理

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	urls := []string{
		"http://tatenosystem.com",
		"http://codegame.tatenosystem.com",
		"http://tatenosystem.com/env",
	}

	for _, url := range urls {
		res, err := http.Get(url)
		if err != nil {
			log.Fatal(err)
		}
		// fmt.Printf("%t", res.Body)
		defer res.Body.Close()
		fmt.Println(url, res.Status)

		// body 表示
		byteArray, _ := ioutil.ReadAll(res.Body) // read.Body は IO I/F
		fmt.Printf(string(byteArray))
	}
}

並行処理

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"time"
	"net/http"
)

func run(url string) {
	res, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	// fmt.Printf("%t", res.Body)
	defer res.Body.Close()
	fmt.Println(url, res.Status)

	// body 表示
	byteArray, _ := ioutil.ReadAll(res.Body) // read.Body は IO I/F
	fmt.Printf(string(byteArray))
}

func main() {
	urls := []string{
		"http://tatenosystem.com",
		"http://codegame.tatenosystem.com",
		"http://tatenosystem.com/env",
	}

	for _, url := range urls {
		go run(url)
	}

	// main() が終わらないように待つ
	time.Sleep(time.Second * 3)
}

sync.WaitGroup でインクリメント/デクリメント

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"sync"
)

func run(url string, wait *sync.WaitGroup) {
	res, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	// fmt.Printf("%t", res.Body)
	defer res.Body.Close()
	fmt.Println(url, res.Status)

	// body 表示
	byteArray, _ := ioutil.ReadAll(res.Body) // read.Body は IO I/F
	fmt.Printf(string(byteArray))
	wait.Done() // デクリメント
}

func main() {
	wait := new(sync.WaitGroup)
	urls := []string{
		"http://tatenosystem.com",
		"http://codegame.tatenosystem.com",
		"http://tatenosystem.com/env",
	}

	for _, url := range urls {
		wait.Add(1) // インクリメント
		go run(url, wait)
	}

	// wait が 0 になるまで待つ
	wait.Wait()
}

チャンネルで結果を取得

package main

import (
	"fmt"
	"log"
	"net/http"
)

func run(url string, statusChan chan string) {
	res, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	// fmt.Printf("%t", res.Body)
	defer res.Body.Close()
	fmt.Println(url, res.Status)

	statusChan <- res.Status
}

func main() {
	statusChan := make(chan string) // チャンネル作成
	urls := []string{
		"http://tatenosystem.com",
		"http://codegame.tatenosystem.com",
		"http://tatenosystem.com/env",
	}

	for _, url := range urls {
		go run(url, statusChan)
	}

	for i := 0; i < len(urls); i++ {
		fmt.Println(<- statusChan)
	}
}

関数の返り値が(呼び出し専用)チャンネル

package main

import (
	"fmt"
	"log"
	"net/http"
)

// 戻り値が chan string( <- で呼び出し専用。main で書き込めない )
func runAll(urls []string) <- chan string {
	statusChan := make(chan string) // チャンネル作成
	for _, url := range urls {

		go func(url string) {
			res, err := http.Get(url)
			if err != nil {
				log.Fatal(err)
			}
			// fmt.Printf("%t", res.Body)
			defer res.Body.Close()
			statusChan <- url+" - "+res.Status
		}(url)
	}

	return statusChan
}

func main() {
	urls := []string{
		"http://tatenosystem.com",
		"http://codegame.tatenosystem.com",
		"http://tatenosystem.com/env",
	}

	statusChan := runAll(urls)

	for i := 0; i < len(urls); i++ {
		fmt.Println(<- statusChan)
	}
}

チャンネルを select で待つ(タイムアウトチャンネル)

package main

import (
	"fmt"
	"log"
	"net/http"
	"time"
)

// 戻り値が chan string( <- で呼び出し専用。main で書き込めない )
func runAll(urls []string) <- chan string {
	statusChan := make(chan string) // チャンネル作成
	for _, url := range urls {

		go func(url string) {
			res, err := http.Get(url)
			if err != nil {
				log.Fatal(err)
			}
			// fmt.Printf("%t", res.Body)
			defer res.Body.Close()
			statusChan <- url+" - "+res.Status
		}(url)
	}

	return statusChan
}

func main() {
	// 2秒後に値が取り出せるチャンネル
	timeout := time.After(time.Second * 2)
	urls := []string{
		"http://tatenosystem.com",
		"http://codegame.tatenosystem.com",
		"http://tatenosystem.com/env/wait/",
	}

	statusChan := runAll(urls)

	LOOP_FOR: // ラベル
	for { // break するまで抜けられないので、2秒待ってしまう
		select {
		case status := <-statusChan:
			fmt.Println(status)
		case <-timeout:
			fmt.Println("TIMEOUT")
			break LOOP_FOR // ラベル指定でbreak
		}
	}

	fmt.Println("END")
}

バッファ付きチャンネル

package main

import (
	"fmt"
	"time"
)

func main() {
	// チャンネルバッファ 5
	ch := make(chan int, 5)
	// チャンネル書き込み
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println("Writing", i)
			ch <- i
		}
		// ch <- -1 // 停止信号
		close(ch) // チャンネルを閉じる
	}()
	
	time.Sleep(time.Second * 2)
	
	// チャンネル読み込み
	for {
		v, ok := <-ch
		if !ok {
			break
		}
		// if v == -1 { break }
		fmt.Println("Read", v)
	}
}

Mutexロック

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	var mu sync.RWMutex
	
	// gorutine
	for i := 1; i <= 5; i++ {
		go func(){
			for i := 0; i < 5; i++ {
				mu.RLock()
				fmt.Printf("Reader %d Acquired lock\n", i)
				time.Sleep(time.Second)
				mu.RUnlock()
			}
		}()
	}

	// 3秒後にロック
	time.AfterFunc(3 * time.Second, func(){
		fmt.Println("LOCK")
		mu.Lock()
	})

	// 6秒後にアンロック
	time.AfterFunc(6 * time.Second, func(){
		fmt.Println("UNLOCK")
		mu.Unlock()
	})

	// 止めておかないと main が終了してしまう
	time.Sleep(time.Second * 9)
}

その他

引数

package main

import (
    "fmt"
    "os"
)

func main() {
    name := os.Args[1]
    s, _ := os.Stat(name) // ファイル取得
    // go run args1.go p.json
    // p.json: 33
    fmt.Printf("%s: %d\n", name, s.Size()) // ファイルサイズ数
}

外部プログラム実行

package main

import (
    "fmt"
    "os/exec"
    "log"
)

func main() {
    out, err := exec.Command("uname", "-a").Output()
    if err != nil { log.Fatal(err) }
    fmt.Printf(string(out))
}

5秒ごとに通知されるチャンネル作成

package main

import (
    "fmt"
    "time"
)

func main() {
    // 5秒ごとに通知されるチャンネル作成
    ch := time.Tick(time.Second * 5)

    for i := 0; i < 10; i++ {
        t := <-ch
        fmt.Println(t)
    }
}