golangのimport文で別名つける
golangのimport文は別名をつけることができる.
普通のimport文は
package main import ( "fmt" //ここ ) func main() { fmt.Println("hello") }
こんな感じで文字列でパッケージ名を指定するだけ.
別名をつける場合には以下のようになる.
package main import ( f "fmt" //ここ ) func main() { f.Println("hello") }
ここまでは知ってたんだけど, パッケージ名指定なしにもできるらしい.
package main import ( . "fmt" //ここ ) func main() { Println("hello") }
.(ドット)ね.あーなるほどね.
_(アンダースコア)の方はdatabase/sqlで割と使う事が多いんじゃないかと思う.
宣言したファイルでは使わないけど, 依存パッケージでは使うよっていう宣言.
import ( "database/sql" // SQL標準のアクセス用パッケージ _ "github.com/go-sql-driver/mysql" // MySQLのドライバ(↑で使う) )
地味なとこで知らないことがおおい...
golangでバイナリを操作する
golangは低レベルな処理(バイナリの取り扱いとかね)もちゃんと行うことができる.
基本(byte型を使う)
golangは組み込みでbyte型が用意されている.
これはその名の通りバイトを扱うための型で, 配列やスライスにすれば単純なバイトストリームを扱える.
package main import ( "fmt" ) func main() { b := []byte{0xDe, 0xaD, 0xBe, 0xeF} fmt.Printf("%b\n", b) // 2進表示 fmt.Printf("%d\n", b) // 10進表示 fmt.Printf("%x\n", b) // 16進小文字表示 fmt.Printf("%X\n", b) // 16進大文字表示 }
[実行結果]
[11011110 10101101 10111110 11101111] [222 173 190 239] deadbeef DEADBEEF
bytesパッケージ
byteのスライスを扱う場面は多いので, 標準で操作するパッケージが用意されている.
(Package bytes)https://golang.org/pkg/bytes/
以下のように比較も簡単だし, 文字列操作を意識しているのかその手の関数が多い.
(byteのスライスはstringとしてみなせるし)
package main import ( "fmt" "bytes" ) func main() { b := []byte{0xDe, 0xaD, 0xBe, 0xeF} b2 := []byte{0xDe, 0xaD, 0x00, 0x00} b3 := []byte{0xDe, 0xaD, 0xBe, 0xeF} c := bytes.Compare(b, b2) // => 1 fmt.Println(c) c2 := bytes.Compare(b, b3) // => 0 fmt.Println(c2) }
[実行結果]
1 0
ファイルに書いてみる
ファイルはただのバイト列なので, ファイルにも書けますね.
package main import ( "io/ioutil" ) func main() { b := []byte{0xDe, 0xaD, 0xBe, 0xeF} ioutil.WriteFile("test.bin", b, 0644) }
[実行結果]
Endianの都合で逆になっちゃってますが...(Windowsはリトルエンディアン)
$ od -x test.bin 0000000 adde efbe 0000004
バッファを扱う(bytes.bufferを使う)
バイトストリームを操作する時にイチイチ操作関数を使っていると面倒でしょうがないです.
io.Readerやio.Writerの操作メソッドを使いたいですね.
あと, いきなりファイルに書いたりせずにバッファリングしたい場面がほとんどです.
bytesパッケージにはbufferという構造体が用意されています.
標準入出力のテストにも使えます.
以下の様なパッケージがあるとして
// 挨拶するパッケージ package hello import ( "fmt" "io" ) func hello(out io.Writer) { fmt.Fprintf(out, "hello, world") }
上記のテストコードは以下になります.
bufferへ書き出して比較するという感じです.
helloの引数にinterfaceを使ったおかげでいい感じになります.
package hello import ( "bytes" "testing" ) func TestHello(t *testing.T) { expected := []byte("hello, world") stdout := new(bytes.Buffer) hello(stdout) if bytes.Compare(stdout.Bytes(), expected) != 0 { t.Fatal("greeting is not matched") } }
このテストは無事通ります.
[実行結果]
$ go test PASS ok _/C_/msys64/home/twinbird/test 0.128s
バイトストリームを構造体へ変換する(encoding/binaryを使う)
プログラムでデータを扱うとなるとやっぱり構造体でまとめたいですね.
一々バイトストリームから構造体へ変換する処理を書くのは面倒ですが,
golangにはいい感じにしてくれるパッケージがあります.
package main import ( "bytes" "fmt" "encoding/binary" ) type Beef struct { One byte Two byte Three byte Four byte } func main() { b := []byte{0xDe, 0xaD, 0xBe, 0xeF} buf := bytes.NewBuffer(b) var beef Beef binary.Read(buf, binary.LittleEndian, &beef) fmt.Printf("%x\n", beef.One) fmt.Printf("%x\n", beef.Two) fmt.Printf("%x\n", beef.Three) fmt.Printf("%x\n", beef.Four) }
[実行結果]
$ ./test.exe de ad be ef
読み出しだけ書いてみましたが, 書き出しもあります.
注意点としては
- 構造体のメンバはちゃんとエクスポートしておかないとダメ
- 渡すのは構造体へのポインタ
というところくらいだと思います.
エンディアンも指定出来て良いです.
golangでfnvハッシュ関数を使う
FNVハッシュ関数は64bit or 32bitでの出力を行うことができるハッシュ関数です.
SHAシリーズ(SHA-1など)やMDシリーズ(MD5など)などとは異なり, 衝突耐性よりも実装が効率的な事を重視しているっぽい.
逆にセキュリティなどを重視するところでは使っちゃダメですね.
サンプルコードをメモっとく.
package main
import (
"fmt"
"hash/fnv"
)
func hash32(message []byte) uint32 {
h := fnv.New32()
h.Write(message)
sum := h.Sum32()
return sum
}
func hash64(message []byte) uint64 {
h := fnv.New64()
h.Write(message)
sum := h.Sum64()
return sum
}
func main() {
fmt.Println(hash32([]byte("Test Hashing")))
fmt.Println(hash64([]byte("Test Hashing")))
}
golangでシグナルをハンドリングする
golangでコマンド作ってもctrl + cとかで止めたくなるじゃないですか.
ちょっと調べた.
package main import ( "fmt" "os" "os/signal" "syscall" ) func main() { sig_ch := make(chan os.Signal, 1) signal.Notify(sig_ch, os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) exit_ch := make(chan int) go func() { for { s := <-sig_ch switch s { case os.Interrupt: // windowsはこれで頑張る fmt.Println("Interrupt") exit_ch <- 0 case syscall.SIGHUP: fmt.Println("SIGHUP") exit_ch <- 0 case syscall.SIGINT: fmt.Println("SIGINT") exit_ch <- 0 case syscall.SIGTERM: fmt.Println("SIGTERM") exit_ch <- 0 case syscall.SIGQUIT: fmt.Println("SIGQUIT") exit_ch <- 0 default: fmt.Println("Other") exit_ch <- 1 } } }() code := <-exit_ch os.Exit(code) }
Windowsはちょっと別枠ですが, ちゃんと取り扱えるのはいいですね.
余談ですが, Windows + msys2だとうまく動かないのでいい方法が知りたいです.
golangでtsv(csv)を読む
探せばいくらでも出てきそうだけど,メモしておく.
コード見たほうが早いと思うので.
package main import ( "encoding/csv" "fmt" "log" "strings" ) func main() { // テスト用文字列 str := "test\tテスト\nHello\tこんにちは" // CSVのReaderを用意 r := csv.NewReader(strings.NewReader(str)) // デリミタ(TSVなら\t, CSVなら,)設定 r.Comma = '\t' // コメント設定(なんとコメント文字を指定できる!) r.Comment = '#' // 全部読みだす records, err := r.ReadAll() if err != nil { log.Fatal(err) } // 各行でループ for _, v := range records { // 1列目 fmt.Print(v[0]) fmt.Print(" | ") // 2列目 fmt.Println(v[1]) } }
実行結果
$ ./tsv.exe test | テスト Hello | こんにちは
割と使いやすいライブラリと思う(クセがない)
vsftpdでディレクトリが削除できない
WindowsからFTPクライアントで(私の場合はエクスプローラで)ディレクトリを削除しようとすると以下のエラーが出た.
550 Create directory operation failed.
本当に原因がわからなくて30分くらい悩んだのだけど,どうも.付きのファイルがFTPで取得できていないのが問題らしい.
そこで/etc/vsftpd/vsftpd.conf内に以下を追記してサービス再起動で解決した.
force_dot_files=YES
マジでハマった.
golangでオブジェクトをシリアライズしたい
cacheに乗せたい時やちょっとしたものを作るときには簡単にシリアライズ/デシリアライズできる パッケージがあると便利だ.
gobパッケージを使うと簡単にできるようだ.
rpc向けのパッケージらしい.
他ではencoding/jsonやencoding/xmlだろうか.ここら辺はググればすぐ出ると思う. encoding/binaryも使い道によっては良いと思う.
それでもgobを使ってみたのはgobの方が早いとの記述を何処かで見たからだ.(が,測定はしていない)
使い方は絶対忘れるので作ったサンプルを書いておく.
package main import ( "bytes" "time" "fmt" "encoding/gob" ) // サンプル用の構造体 // 何となくブログ風味 type Entry struct { ID int64 `datastore:"-"` Title string Contents string CreateAt time.Time UpdateAt time.Time DeleteAt time.Time } // シリアライズ用のメソッド // レシーバ(e)の値をシリアライズしてbyte配列にする func (e *Entry) GobEncode() ([]byte, error) { // エンコーダを作る. // io.Writerであれば何でも良いのでここではbytes.Bufferを使う w := new(bytes.Buffer) encoder := gob.NewEncoder(w) // 先頭から順番にエンコードしていく // 必ず先頭から1つずつエンコードを実行していく必要があるらしい if err := encoder.Encode(e.ID); err != nil { return nil, err } if err := encoder.Encode(e.Title); err != nil { return nil, err } if err := encoder.Encode(e.Contents); err != nil { return nil, err } if err := encoder.Encode(e.CreateAt); err != nil { return nil, err } if err := encoder.Encode(e.UpdateAt); err != nil { return nil, err } if err := encoder.Encode(e.DeleteAt); err != nil { return nil, err } return w.Bytes(), nil } // デシリアライズするためのデコーダ // byte配列を渡してレシーバへ値を入れる func (e *Entry) GobDecode(buf []byte) error { // デコーダを作る. // io.Readerであれば何でも良いのでここではbytes.Bufferを使う r := bytes.NewBuffer(buf) decoder := gob.NewDecoder(r) // 先頭から順番にデコードしていく // 必ず先頭から1つずつデコードを実行していく必要があるらしい if err := decoder.Decode(&e.ID); err != nil { return err } if err := decoder.Decode(&e.Title); err != nil { return err } if err := decoder.Decode(&e.Contents); err != nil { return err } if err := decoder.Decode(&e.CreateAt); err != nil { return err } if err := decoder.Decode(&e.UpdateAt); err != nil { return err } if err := decoder.Decode(&e.DeleteAt); err != nil { return err } return nil } func main() { // 記録するデータ e1 := &Entry{ ID: 1, Title: "title", Contents: "contents", CreateAt: time.Now(), UpdateAt: time.Now(), DeleteAt: time.Now(), } // bにシリアライズして入れる b, err := e1.GobEncode() if err != nil { fmt.Println("Error occured.", err) } // デシリアライズするために用意 e2 := &Entry{} // bからe2へデシリアライズ if err = e2.GobDecode(b); err != nil { fmt.Println("Error occured.", err) } // デシリアライズしたものを表示 fmt.Println(e2.ID) fmt.Println(e2.Title) fmt.Println(e2.Contents) fmt.Println(e2.CreateAt) fmt.Println(e2.UpdateAt) fmt.Println(e2.DeleteAt) }
コメントも書いたし,多分思い出せるだろう.