Goで詰まった所、ググった所メモ


何度も調べるの面倒なので調べるついでにメモしていく。随時追加予定、順序は適当。

### UnixTimeのepocミリ秒の計算

timeパッケージには`Unix()`と`UnixNano()`しかメソッドがないので、ミリ秒で求めるにはミリ秒除算が必要。

```
import "time"

unixMilliSecond := time.Now().UnixNano() / int64(time.MilliSecond)
```

### string -> []byte変換、またはその逆

キャストすればいい。

```
str := "Lorem Ipsum"
bytes := []byte(str)

str := string(bytes)
```

### integer -> string変換、またはその逆

strconvパッケージを使う。

```
import strconv
```

```
n   := 100
str := strconv.Itoa(n)
```

int64などは`Itoa()`では変換できないので、`FormatInt()`を使う。

```
n   := int64(100)
str := strconv.Itoa(n) // エラー

str := strconv.FormatInt(n, 10)
```

`Atoi()`で文字列に。

```
n = strconv.Atoi(n)
```

#### 追記 -------------------

`fmt.Sprint(n)`でもできる。

```
n := 100
str := fmt.Sprint(n)
```


### Sha1、Sha256

`crypto/sha1`、`crypto/sha256`パッケージを使う。

```
import "crypto/sha1"
```

```
str := "Lorem Ipsum"
encoder := sha1.New()
encoder.Write([]byte(str))
hash := encoder.Sum(nil)

fmt.Printf("%s sha1 crypto to %x", str, hash)
// -> "Lorem Ipsum encoded to 0646164d30b3bd0023a1e6878712eb1b9b15a1da"
```

`[]byte`に変換してWriteしないといけないので注意。あと、`%x`はhexなので、実際のhashはバイナリの[]byteになっている。hexで欲しい時は`encoding/hex`パッケージを使って別途エンコードする。

```
import (
	"crypto/sha1"
	"encoding/hex"
)
```

```
str := "Lorem Ipsum"
encoder := sha1.New()
encoder.Write([]byte(str))
hash := encoder.Sum(nil)
hexHash := hex.EncodeToString(hash)

fmt.Printf("%s sha1 crypto to %s", str, hexHash)
// -> "Lorem Ipsum encoded to 0646164d30b3bd0023a1e6878712eb1b9b15a1da"
```

#### 追記 -------------------

`sha1.Sum([]byte)`直接できる、とのこと（なんで知らなかったのか…）

```
str := "Lorem Ipsum"
hash := sha1.Sum([]byte(str))

fmt.Printf("%s sha1 crypto to %x", str, hash)
// -> "Lorem Ipsum encoded to 0646164d30b3bd0023a1e6878712eb1b9b15a1da"
```

### JSONエンコード、フィールド名指定

`encoding/json`を使う。

```
import "encoding/json"
```

```
// JSON用
type JsonResponse struct {
	UserId  int
    Name    string
}

...

response =: &JsonResponse{
	UserId: 1,
	Name: "ysugimoto",
}

encoded, _ := json.Marshal(response)
fmt.Println(encoded)
// -> {"UserId":"1","Name":"ysugimoto"}
```

このままだとフィールド名がキャメルケースになってしまうので、バッククオートでフィールド名を指定できる。

```
// JSON用
type JsonResponse struct {
	UserId  int      `json:"userid"`
    Name    string   `json:"name"`
}

...

response =: &JsonResponse{
	UserId: 1,
	Name: "ysugimoto",
}

encoded, _ := json.Marshal(response)
fmt.Println(encoded)
// -> {"userid":"1","name":"ysugimoto"}
```

### HTTPリクエスト送信

`net/http`、`bytes`パッケージを使う。

```
import (
	"net/http"
	"bytes"
)
```

```
data := "this is data"
req, err := http.NewRequest("POST", "http://localhost:9001", bytes.NewBuffer([]byte(data))
req.Header.Set("Content-Type", "application/www-x-form-urlencoded")

client := &http.Client{}
resp, reqErr := client.Do(req)
if reqErr != nil {
	fmt.Printf("%v", reqErr)
}
resp.Body.Close()
```

最後に`Close()`するのを忘れずに。`defer`に登録してもいいのかな。

### スライスの可変長引数展開渡し

可変長引数を受け付ける関数には、スライスを`...`で展開して渡せる。

```
func main() {
    // 普通にintを複数渡す
    total := sum(10, 1, 2, 3)
	fmt.Println(total)  // 16

    // スライスを展開して渡す
	args := []int{1, 2, 3}
	total2 := sum(10, args...)
	fmt.Println(total2)  // 16
}

// 第二引数以降が可変長の関数
func sum(base int, args ...int) int {
	fmt.Println(args)

	for i := range args {
		base += i
	}

	return base
}
```

SQL文でプレースホルダに値をバインドするときにこのやり方が必要だった。

### 乱数生成

`math/rand`パッケージを使う。乱数シードにタイムスタンプ使う場合。

```
import "math/rand"
import "time"
```

```
rand.Seed(time.Now().Unix())
r := rand.Int31() // 32bit-Int乱数

r := rand.Intn(10) // 0から10の範囲指定乱数
```

### Hash-Hmacとか

`crypto/hmac`を使う。`crypto/sha1`、`crypto/sha256`は用途に応じて。

```
import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
    "fmt"
)
```

```
secret := "foobar"
str := "Lorem Ipsum"
hash := hmac.New(sha256.New, []byte(secret))
hash.Write([]byte(str))

fmt.Println(hex.EncodeToString(hash.Sum(nil))
```

### コマンドラインフラグ

`flag`パッケージを使う。`IntVar()`などで変数にバインドする方法と、`Int()`で変数の戻り値を使う方法がある。

```
import "flag"
```

```
// 変数にバインドするパターン
var num int
flag.IntVar(&num, "n", 0, "Number")
fmt.Println(num)

// 変数の戻り値を使うパターン
num := flag.Int("n", 0, "Number")
fmt.Println(num)
```

いずれも`num`はコマンドラインの`-n`オプションの値をポインタとして受け取る。実行するときは、フラグ名の後ろに半角スペース、またはイコールを付けないと正しく受け取れない。すなわち、

```
$ go run sample.go -n 10
>> 10
$ go run sample.go -n=10
>> 10
```

だが、

```
$ go run sample.go -n10
>> error
```

となるので注意。

### コマンドライン引数

`os.Args []string`から取得できる。が、上記のフラグも一緒に取ってしまうので、純粋な引数リストのみはフィルタリングしないといけなかった。そこで、別でフィルタリング用の関数を作って対処した。

```
import "os"
```

```
func main() {
    // os.Args[0]はコマンド自身なので無視
	arguments := parseArguments(os.Args[1:])
    fmt.Printf("Arguments: %v\n", arguments)
}

// "-"がついた引数はフラグとしてスキップする
func parseArguments(args []string) (parsed []string) {
	var r []rune
	flagValue := false
	for _, arg := range args {
		r = []rune(arg)
		if string(r[0]) == "-" {
			flagValue = true
			continue
		} else if flagValue {
			flagValue = false
			continue
		}
		parsed = append(parsed, arg)
	}
	
	return
}
```

戻り値が初期化済みなので、そのまま`return`すればOK。これ初めて見た時すげーって思った。あと、この関数では単純にスイッチになっている引数が取れない(-sのみのフラグなど）。何かいい方法あれば教えて下さい…

```
$ go run sample.go -n 10 foo
>> Arguments: [foo]
```

### ループ

`Map`と`Array`、`Slice`は`range`式でループできる。range式はそれぞれ2値を戻す。

```
m := map[string]string{"foo": "bar", "Lorem": "Ipsum"}

// キー、値でループできる
for key, value := range m {
	fmt.Printf("Key is %s, Value is %s\n", key, value)
}

s := []string{"foo", "bar", "baz"}
// インデックス、値でループできる
for ind, value := range s {
	fmt.Printf("Index is %d, Value is %s\n",ind, value)
}
```

```
$ go run loop.go
Key is foo, Value is bar
Key is Lorem, Value is Ipsum
Index is 0, Value is foo
Index is 1, Value is bar
Index is 2, Value is baz
```

### マップやスライスの存在チェック

マップは2つ目の値がnilかどうかで判定できる。スライスは`len()`でサイズ判定すればいいのかな？

```
m := map[string]string{"foo": "bar", "Lorem": "Ipsum"}

// 存在チェック
if v, ok := m["baz"]; ok {
   // 存在した時の処理
}

s := []string{"foo", "bar", "baz"}
// 存在チェック
if len(s) > 1 {
   // 存在した時の処理
}
```

このあたりどうするのがベストなんだろう。

### キーボード入力

`fmt.Scanln()`で入力待ち状態できる。

```
var input string
fmt.Print("What's your name?")
fmt.Scanln(&input)

fmt.Printf("Hello, %s!\n", input)
```

### 現在実行中のファイルパスの取得

いわゆる`__FILE__`的なことがしたいときは`runtime`パッケージを使う。

```
import "runtime"

…

_, filename, _, _ := runtime.Caller(1)
fmt.Println(filename) // 実行中のcurrent file pathが取得できる
```

`runtime.Caller()`では他にも行番号も取得できる。`path.Dir`と組み合わせれば`__DIR__`もできるね。

### 日付処理

`time`パッケージを使う。かなり独特の仕組みになってるけど、理解してしまえばなんのことはない。年月日などが特定の識別子になっていて、それを組み合わせてフォーマッタに使えばいい。見た感じ、識別子は以下の様な感じ。

- 年 "2006"
- 月 "01" または "Jan"
- 日 "02"
- 時 "15"
- 分 "04"
- 秒 "05"
- 時差 "-0700" とか "-07:00" とか
- 曜日 "Mon"

例えば、MySQLのDATETIME型に現在時刻を入れる時は以下のような感じで。

```
import "time"

dateTime := t.Now().Format("2006-01-02 15:04:05")
// Insert...
```

### Gzip

オンザフライで作って圧縮後のサイズを読み取る処理。普通にファイルを操作するのはググれば出てくるので、動的に圧縮してデータを取得するサンプル。io.Writer/io.Readerをよくわかってない疑惑。

```
import "compress/gzip"
import "bytes"
import "io/ioutil"
import "fmt"

var data = []byte("0000111122223333444455556666777788889999")

func main() {
	var b bytes.Buffer

	// 圧縮
	writer := gzip.NewWriter(&b) // NewWriterLevelでもいい
	writer.Write(data)
	
	// Flush/Closeしてからでないとバイトが取得できない
    if err := writer.Flush(); err != err {
		fmt.Printf("%w\n", err)
		return
	}
	if err := writer.Close(); err != err {
		fmt.Printf("%w\n", err)
		return
	}
	// 読み取り
	compressed = b.Bytes()
	fmt.Println(compressed)
}
```