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

  • 40
    いいね
  • 3
    コメント
この記事は最終更新日から1年以上が経過しています。

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

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/sha1crypto/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/httpbytesパッケージを使う。

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/sha1crypto/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]

ループ

MapArraySlicerange式でループできる。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)
}