- 投稿日:2020-11-25T23:25:26+09:00
Golangでtomlを読み込む
概要
dockerコンテナでgolangを動かしtomlを読み込んで標準フォーマットで出力する。
ファイル構成
今回はdockerを使ってコンテナ上で動かしますので以下のファイル構成で行うこととします。
goTest ├docker-compose.yml ├Dockerfile └src ├main.go ├config │ └testConfig.toml └tomlexec ├tomlexec.go └tomlexec_test.go登場ファイル
主な登場ファイルはdockerファイルを除けば難しい事はなく、以下の4ファイルになります。
・main.go
・testConfig.toml
・tomlexec.go
・tomlexec_test.go各ファイルの設定を見ていきましょう。
docker-compose.yml
docker-compose.ymlversion: '3.7' services: #golangの設定 app: build: ./ container_name: goTest tty: true volumes: - ../:/go/src/charts_server/src working_dir: /go/src/charts_server/src/goTest/src ports: - "8080:8080" command: go run main.go environment: - "GOPATH=/go/src/charts_server"解説
細かい説明は省きますが、以下の通りとなります。
volumes:localのディレクトリとdockerコンテナのディレクトリの同期をここで行っています。
working_dir:ここで基準になるディレクトリを設定しています。
ports:アクセスする際のポートナンバーです。
environment:ここではdockerコンテナ内でのGOPATHを指定しています。今回は「/go/src/charts_server」Dockerfile
FROM golang:latest RUN mkdir /go/src/charts_server WORKDIR /go/src/charts_server/src/goTest/src ADD . /go/src/charts_serverここに関してはdockerの設定を行っていますが、シンプルなものとなっていますので説明は不要かと思います。
main.go
main.gopackage main import ( "goTest/src/tomlexec" ) func main() { tomlexec.Exec() }解説
とてもシンプルです。
1階層下のパッケージ、tomlexec
をインポートして、tomlexecパッケージのExec
関数を呼び出しているだけです。testConfig.toml
testConfig.toml[TestToml] No = 1 Name = "testName"解説
今回はgoの構造体に合わせて上記のような設定をしています。
Noはint、Nameはstringとなっています。
TestToml
に関してはmapで言うkeyのようなものと思えば良いと思います。
TestToml
と言うキーの中に、No
、Name
と言うmapがあると思えば想像しやすいかもしれません。tomlExec.go
tomlExec.gopackage tomlexec import ( "fmt" "github.com/BurntSushi/toml" ) // TConfig ... type TConfig struct { TestToml *TomlConfig } // TomlConfig ... type TomlConfig struct { No int Name string } // Exec ... func Exec() { var cfg TConfig _, err := toml.DecodeFile("./config/testConfig.toml", &cfg) if err != nil { fmt.Println(err) } fmt.Println(cfg.TestToml.Name) }解説
TConfig
、TomlConfig
はそれぞれ、構造体の定義を行っています。
toml.DecodeFile
で読み込むtomlファイルと読み込ませる構造体を指定しています。
今回は読み込んだ後にName
だけ標準出力するようにしています。tomlexec_test.go
tomlexec_test.gopackage tomlexec import ( "os" "path/filepath" "testing" ) func TestExec(t *testing.T) { apath, _ := filepath.Abs("../") os.Chdir(apath) Exec() }解説
tomlExecパッケージのExec関数を呼んでいるだけの簡単なtestです。
ポイントはfilepath.Abs("../")
とos.Chdir
。
これをやらないとエラーとなってしまいます。
理由はmain.goの実行場所とこのテストの実行場所が違うと言うことです。
最初に挙げたファイル構成を見て頂けるとわかりますが、main.goよりも1階層深い場所にこのテストがあります。
このまま実行すると./config/testConfig.toml
がどこにあるのかプログラム上では分からなくなり、エラーとなりますが、filepath.Abs("../")
で1階層下の絶対パスを取得し、それを引数にos.Chdir
でワーキングディレクトリを指定することで同じように読むことが可能となります。実行
goTest> docker-compose up Starting goTest ... done Attaching to goTest goTest | testName
testName
が表示されているのでしっかり読めていますね。テスト
手順は
1.起動しているコンテナを検索
2.コンテナの中に入る
3.テストのあるディレクトリまで移動してテストを実行
となります。1.起動しているコンテナを検索goTest> docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c38c50f20df5 gotest_app "go run main.go" 24 hours ago Up 27 minutes 0.0.0.0:8080->8080/tcp goTest2.コンテナの中に入るgoTest> docker exec -ti c38c50f20df5 bash root@c38c50f20df5:/go/src/charts_server/src/goTest/src#3.テストのあるディレクトリまで移動してテストを実行goTest/src# ls charts_server config main.exe main.go tmp tomlexec goTest/src# cd tomlexec/ goTest/src/tomlexec# go test -v === RUN TestExec testName --- PASS: TestExec (0.01s) PASS ok goTest/src/tomlexec 0.011sここまでで終了です。
テストも通りました。一応上記のコードはgitで公開していますので、見たい方は参考までにどうぞ。
- 投稿日:2020-11-25T23:25:26+09:00
docker、Golangでtomlを読み込む
概要
dockerコンテナでgolangを動かしtomlを読み込んで標準フォーマットで出力する。
ファイル構成
今回はdockerを使ってコンテナ上で動かしますので以下のファイル構成で行うこととします。
goTest ├docker-compose.yml ├Dockerfile └src ├main.go ├config │ └testConfig.toml └tomlexec ├tomlexec.go └tomlexec_test.go登場ファイル
主な登場ファイルはdockerファイルを除けば難しい事はなく、以下の4ファイルになります。
・main.go
・testConfig.toml
・tomlexec.go
・tomlexec_test.go各ファイルの設定を見ていきましょう。
docker-compose.yml
docker-compose.ymlversion: '3.7' services: #golangの設定 app: build: ./ container_name: goTest tty: true volumes: - ../:/go/src/charts_server/src working_dir: /go/src/charts_server/src/goTest/src ports: - "8080:8080" command: go run main.go environment: - "GOPATH=/go/src/charts_server"解説
細かい説明は省きますが、以下の通りとなります。
volumes:localのディレクトリとdockerコンテナのディレクトリの同期をここで行っています。
working_dir:ここで基準になるディレクトリを設定しています。
ports:アクセスする際のポートナンバーです。
environment:ここではdockerコンテナ内でのGOPATHを指定しています。今回は「/go/src/charts_server」Dockerfile
FROM golang:latest RUN mkdir /go/src/charts_server WORKDIR /go/src/charts_server/src/goTest/src ADD . /go/src/charts_serverここに関してはdockerの設定を行っていますが、シンプルなものとなっていますので説明は不要かと思います。
main.go
main.gopackage main import ( "goTest/src/tomlexec" ) func main() { tomlexec.Exec() }解説
とてもシンプルです。
1階層下のパッケージ、tomlexec
をインポートして、tomlexecパッケージのExec
関数を呼び出しているだけです。testConfig.toml
testConfig.toml[TestToml] No = 1 Name = "testName"解説
今回はgoの構造体に合わせて上記のような設定をしています。
Noはint、Nameはstringとなっています。
TestToml
に関してはmapで言うkeyのようなものと思えば良いと思います。
TestToml
と言うキーの中に、No
、Name
と言うmapがあると思えば想像しやすいかもしれません。tomlExec.go
tomlExec.gopackage tomlexec import ( "fmt" "github.com/BurntSushi/toml" ) // TConfig ... type TConfig struct { TestToml *TomlConfig } // TomlConfig ... type TomlConfig struct { No int Name string } // Exec ... func Exec() { var cfg TConfig _, err := toml.DecodeFile("./config/testConfig.toml", &cfg) if err != nil { fmt.Println(err) } fmt.Println(cfg.TestToml.Name) }解説
TConfig
、TomlConfig
はそれぞれ、構造体の定義を行っています。
toml.DecodeFile
で読み込むtomlファイルと読み込ませる構造体を指定しています。
今回は読み込んだ後にName
だけ標準出力するようにしています。tomlexec_test.go
tomlexec_test.gopackage tomlexec import ( "os" "path/filepath" "testing" ) func TestExec(t *testing.T) { apath, _ := filepath.Abs("../") os.Chdir(apath) Exec() }解説
tomlExecパッケージのExec関数を呼んでいるだけの簡単なtestです。
ポイントはfilepath.Abs("../")
とos.Chdir
。
これをやらないとエラーとなってしまいます。
理由はmain.goの実行場所とこのテストの実行場所が違うと言うことです。
最初に挙げたファイル構成を見て頂けるとわかりますが、main.goよりも1階層深い場所にこのテストがあります。
このまま実行すると./config/testConfig.toml
がどこにあるのかプログラム上では分からなくなり、エラーとなりますが、filepath.Abs("../")
で1階層下の絶対パスを取得し、それを引数にos.Chdir
でワーキングディレクトリを指定することで同じように読むことが可能となります。
※pathが崩れてtestが通らない所を、通すのが目的なのでassertなどは記載していません。実行
goTest> docker-compose up Starting goTest ... done Attaching to goTest goTest | testName
testName
が表示されているのでしっかり読めていますね。テスト
手順は
1.起動しているコンテナを検索
2.コンテナの中に入る
3.テストのあるディレクトリまで移動してテストを実行
となります。1.起動しているコンテナを検索goTest> docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c38c50f20df5 gotest_app "go run main.go" 24 hours ago Up 27 minutes 0.0.0.0:8080->8080/tcp goTest2.コンテナの中に入るgoTest> docker exec -ti c38c50f20df5 bash root@c38c50f20df5:/go/src/charts_server/src/goTest/src#3.テストのあるディレクトリまで移動してテストを実行goTest/src# ls charts_server config main.exe main.go tmp tomlexec goTest/src# cd tomlexec/ goTest/src/tomlexec# go test -v === RUN TestExec testName --- PASS: TestExec (0.01s) PASS ok goTest/src/tomlexec 0.011sここまでで終了です。
テストも通りました。一応上記のコードはgitで公開していますので、見たい方は参考までにどうぞ。
- 投稿日:2020-11-25T23:22:18+09:00
GoのEchoでオリジン間リソース共有の問題を解決
やりたいこと
フロント側の
http://localhost:8080/
からバックエンド側のhttp://localhost:8080/
を呼び出したい
しかし以下のエラーがフロント側で出ていたので、これを解決したいAccess to XMLHttpRequest at 'http://localhost:8000' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.オリジン管理ソース共有に関してはこの記事では解説しませんが、 こちらの記事 がわかりやすかったです
環境情報
- vue
- 2.6
- go
- 1.15
- echoをサーバーとして使用
解決方法
以下の設定を追加
e := echo.New() // 以下を追加 e.Use(middleware.CORSWithConfig(middleware.CORSConfig{ AllowOrigins: []string{"http://localhost:8080"}, AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodPost, http.MethodDelete}, }))無事フロント側からアクセスできる様になりました!
参考
- なんとなく CORS がわかる...はもう終わりにする。
- CORS Middleware
- 投稿日:2020-11-25T18:49:28+09:00
[競プロ]Goの標準入力をまとめる
はじめに
ある日Paizaにハマったので、Goの標準入力についてまとめておく。
1つのデータの入力
package main import "fmt" func main() { var s string fmt.Scan(&s) fmt.Println(s) }1行のデータの入力
package main import ( "bufio" "fmt" "os" ) func main() { reader := bufio.NewReader(os.Stdin) s, _ := reader.ReadString('\n') fmt.Println(s) }n行のデータの入力(1行目にデータ数)
package main import ( "bufio" "fmt" "os" "strconv" "strings" ) func main() { reader := bufio.NewReader(os.Stdin) s, _ := reader.ReadString('\n') s = strings.TrimSpace(s) n, _ := strconv.Atoi(s) for i:=0; i<n; i++ { s, _ = reader.ReadString('\n') s = strings.TrimSpace(s) fmt.Println(s) } }複数のデータ(今回は3つ)の入力(スペース区切り)
package main import ( "bufio" "fmt" "os" "strings" ) func main() { reader := bufio.NewReader(os.Stdin) s, _ := reader.ReadString('\n') s = strings.TrimSpace(s) t := strings.Split(s, " ") fmt.Println(t[0]) fmt.Println(t[1]) fmt.Println(t[2]) }n個のデータの入力(1行目にデータ数, 2行目にn個のデータ(スペース区切り))
package main import ( "bufio" "fmt" "os" "strconv" "strings" ) func main() { reader := bufio.NewReader(os.Stdin) s, _ := reader.ReadString('\n') s = strings.TrimSpace(s) n, _ := strconv.Atoi(s) s, _ = reader.ReadString('\n') s = strings.TrimSpace(s) t := strings.Split(s, " ") for i:=0; i<n; i++ { fmt.Println(t[i]) } }行列の作成
package main import ( "bufio" "fmt" "os" "strconv" "strings" ) func main() { var matrix[][] string reader := bufio.NewReader(os.Stdin) s, _ := reader.ReadString('\n') s = strings.TrimSpace(s) t := strings.Split(s, " ") n, _ := strconv.Atoi(t[0]) for i:=0; i<n; i++ { s, _ = reader.ReadString('\n') s = strings.TrimSpace(s) x := strings.Split(s, "") matrix = append(matrix, x) } fmt.Println(matrix) }
- 投稿日:2020-11-25T16:05:20+09:00
golangで先週の〇〇曜日をtime.Timeで取得するコード
type Weekday struct { Now time.Time Loc *time.Location } func (w Weekday) LastWeekOf(weekday time.Weekday) time.Time { levellingOffset := int(w.Now.Weekday()) weekDiff := 7-(int(weekday)) return time. Date(w.Now.Year(), w.Now.Month(), w.Now.Day(), 0, 0, 0, 0, w.Loc). AddDate(0, 0, -levellingOffset). AddDate(0, 0, -weekDiff) }こう使う
package main import ( "fmt" "time" ) func main() { now := time.Date(2020, 11, 27, 0, 0, 0, 0, time.UTC) fmt.Println(now.Year(), now.Month(), now.Day()) w := Weekday{ Now: now, Loc: time.UTC, } r := w.LastWeekOf(time.Monday) // 先週の月曜日を取得する fmt.Println(r.Year(), r.Month(), r.Day()) }結果
2020 November 27 2020 November 16
- 投稿日:2020-11-25T15:36:48+09:00
細かいことは置いといて、とりあえずProtocolBuffersを触ってみたい時の覚書
- タイトルの通り、自分が試した方法をまとめておく
- ProtocolBuffersとはなんぞや?に関しては公式とか色んな方がネットに情報上げてくれてるので割愛
環境構築
- ubuntu18.04
- Go1.14
- ディレクトリ構成
/home/user/
└── go/
├── bin/
├── pkg/
└── src/
├── go.mod
├── go.sum
├── example.go
├── example.proto
└── pb/
└── (protoファイルのコンパイル後のファイルをここに置く)
まずprotocのインストール(ダウンロード先)
/home/user/# protocのzipファイルをwget $ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.14.0/protoc-3.14.0-linux-x86_64.zip # zipファイル解凍 $ unzip protoc-3.14.0-linux-x86_64.zip # protocのバイナリをbin配下に移動 $ sudo mv bin/protoc /usr/local/bin/ # includeも移動(しておかないといけないらしい、参照するから) $ sudo mv include/ /usr/local/これでインストールされたはず
/home/user# 確認 $ protoc --version libprotoc 3.14.0
次にprotoc-gen-goのインストール
# インストール $ go get -u google.golang.org/protobuf/cmd/protoc-gen-goこれで完了
# 確認 $ protoc-gen-go --version protoc-gen-go v1.25.0※ Command 'protoc-gen-go' not found... なんて表示されたら(僕の環境の場合)
/home/user/# 下記をvim等で.profileに追加、PATHを通す export PATH="$HOME/go/bin:$PATH" # 反映 $ source .profileprotoファイル作成
- message → フィールドというか値をまとめたもの
- enum → define定義のようなもの
- repeated → 配列
- = 番号 → タグのようなもの。連番で付ける(messageは1から。enumは0から)
※ option go_packageはなくても問題なさそうですが、WARNINGが出るので、こちら参考にさせていただきました
→ Protocol Buffersのgo_packageの;の仕様go/src/example.protosyntax = "proto3"; package example; option go_package = ".;pb"; message User { int32 id = 1; string name = 2; enum AddressType { HOKKAIDO = 0; TOHOKU = 1; KANTO = 2; CHUBU = 3; KINKI = 4; CHUGOKU = 5; SHIKOKU = 6; KYUSYU = 7; OKINAWA = 8; } message Address { string area = 1; AddressType type = 2; } repeated Address addresses = 3; }protoファイルのコンパイル
- 「-I」→ アプリケーションのソースコードが存在する場所
- 「--go_out」→ コンパイルで自動生成されるpb.goファイルの配置先
go/src/# protocコマンドでコンパイル $ protoc -I=. --go_out=./pb example.protoシリアライズとデシリアライズ
Goでシリアライズとデシリアライズを行う
※ コードはこちら参考にさせていただきました
→ Protocol Buffers 導入メモ Mac/Wingo/src/example.gopackage main import ( "fmt" pb "src/pb" "github.com/golang/protobuf/proto" ) func main() { // シリアライズ obj := &pb.User{ Id: 123, Name: "yuuzin217", Addresses: []*pb.User_Address{ { Area: "Tokyo", Type: pb.User_KANTO, }, { Area: "Kyoto", Type: pb.User_KINKI, }, }, } buff, err := proto.Marshal(obj) if err != nil { fmt.Println("シリアライズ失敗", err) return } fmt.Println("シリアライズ結果:", buff) // デシリアライズ parseResult := &pb.User{} err = proto.Unmarshal(buff, parseResult) if err != nil { fmt.Println("デシリアライズ失敗: ", err) return } fmt.Println("デシリアライズ結果 id: ", parseResult.Id) fmt.Println("デシリアライズ結果 name: ", parseResult.Name) fmt.Println("デシリアライズ結果 addresses: ", parseResult.Addresses[1].Area) }
保存して実行
go/src/# 実行 $ go run example.go シリアライズ結果: [8 123 18 9 121 117 117 122 105 110 50 49 55 26 9 10 5 84 111 107 121 111 16 2 26 9 10 5 75 121 111 116 111 16 4] デシリアライズ結果 id: 123 デシリアライズ結果 name: yuuzin217 デシリアライズ結果 addresses: Kyoto参考
- 投稿日:2020-11-25T15:36:48+09:00
ProtocolBuffersの覚書
転職してProtocolBuffersの勉強をする機会があったので、最低限のところだけ忘れないようにまとめる。
環境構築
- ubuntu18.04
- Go1.14
- ディレクトリ構成
/home/user/
└── go/
├── bin/
├── pkg/
└── src/
├── go.mod
├── go.sum
├── example.go
├── example.proto
└── pb/
└── (protoファイルのコンパイル後のファイルをここに置く)
まずprotocのインストール(ダウンロード先)
/home/user/# protocのzipファイルをwget $ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.14.0/protoc-3.14.0-linux-x86_64.zip # zipファイル解凍 $ unzip protoc-3.14.0-linux-x86_64.zip # protocのバイナリをbin配下に移動 $ sudo mv bin/protoc /usr/local/bin/ # includeも移動(しておかないといけないらしい、参照するから) $ sudo mv include/ /usr/local/これでインストールされたはず
/home/user# 確認 $ protoc --version libprotoc 3.14.0
次にprotoc-gen-goのインストール
# インストール $ go get -u google.golang.org/protobuf/cmd/protoc-gen-goこれで完了
# 確認 $ protoc-gen-go --version protoc-gen-go v1.25.0※ Command 'protoc-gen-go' not found... なんて表示されたら(僕の環境の場合)
/home/user/# 下記をvim等で.profileに追加、PATHを通す export PATH="$HOME/go/bin:$PATH" # 反映 $ source .profileprotoファイル作成
- message → フィールドというか値をまとめたもの
- enum → define定義のようなもの
- repeated → 配列
※ option go_packageはなくても問題なさそうですが、WARNINGが出るので、こちら参考にさせていただきました
→ Protocol Buffersのgo_packageの;の仕様go/src/example.protosyntax = "proto3"; package example; option go_package = ".;pb"; message User { int32 id = 1; string name = 2; enum AddressType { HOKKAIDO = 0; TOHOKU = 1; KANTO = 2; CHUBU = 3; KINKI = 4; CHUGOKU = 5; SHIKOKU = 6; KYUSYU = 7; OKINAWA = 8; } message Address { string area = 1; AddressType type = 2; } repeated Address addresses = 3; }protoファイルのコンパイル
- 「-I」→ アプリケーションのソースコードが存在する場所
- 「--go_out」→ コンパイルで自動生成されるpb.goファイルの配置先
go/src/# protocコマンドでコンパイル $ protoc -I=. --go_out=./pb example.protoシリアライズとデシリアライズ
Goでシリアライズとデシリアライズを行う
※ コードはこちら参考にさせていただきました
→ Protocol Buffers 導入メモ Mac/Wingo/src/example.gopackage main import ( "fmt" pb "src/pb" "github.com/golang/protobuf/proto" ) func main() { // シリアライズ obj := &pb.User{ Id: 123, Name: "yuuzin217", Addresses: []*pb.User_Address{ { Area: "Tokyo", Type: pb.User_KANTO, }, { Area: "Kyoto", Type: pb.User_KINKI, }, }, } buff, err := proto.Marshal(obj) if err != nil { fmt.Println("シリアライズ失敗", err) return } fmt.Println("シリアライズ結果:", buff) // デシリアライズ parseResult := &pb.User{} err = proto.Unmarshal(buff, parseResult) if err != nil { fmt.Println("デシリアライズ失敗: ", err) return } fmt.Println("デシリアライズ結果 id: ", parseResult.Id) fmt.Println("デシリアライズ結果 name: ", parseResult.Name) fmt.Println("デシリアライズ結果 addresses: ", parseResult.Addresses[1].Area) }
保存して実行
go/src/# 実行 $ go run example.go シリアライズ結果: [8 123 18 9 121 117 117 122 105 110 50 49 55 26 9 10 5 84 111 107 121 111 16 2 26 9 10 5 75 121 111 116 111 16 4] デシリアライズ結果 id: 123 デシリアライズ結果 name: yuuzin217 デシリアライズ結果 addresses: Kyoto参考
- 投稿日:2020-11-25T10:36:41+09:00
とにかくGolangでAvroを使わせろ
概要
Apache Avroにはデータ・スキーマを用いて永続化や通信を行うデータを圧縮する為の仕様「A compact, fast, binary data format.(コンパクトで高速なバイナリ形式データ)」があります。
本記事ではAvroの詳細まで言及しませんが、この仕様をGolangで実装して圧縮されたデータを実感してみたいと思います。データ圧縮の準備
Avroスキーマ
Avroバイナリデータに圧縮するには、予めデータ構造のスキーマ定義を作成する必要があります。
まずは2つの項目を持つ簡単なスキーマを定義します。
(後ほどGolangのソースコード内でstringデータとして使用します){ "namespace": "TestSchema", "type": "record", "name": "TestData", "fields" : [ { "name" : "id", "type" : "int" }, { "name" : "value", "type" : "string" } ] }圧縮前のデータ(JSON)
スキーマにはint型の"id"と言う名前の項目とstring型の"value"と言う2つの項目が定義されています。
この定義に沿った以下のJSONデータを作成します。{"id":10001,"value":"test_value"}データ圧縮ロジック
Goライブラリ
以下のコマンドを実行してライブラリを取得します。
go get "github.com/linkedin/goavro"シリアライズ処理手順
以下の手順で実行します。
// 1.JSON文字列をGolangのプリミティブ型のデータへ変換 // 1-1.スキーマを設定したCodecを生成 codec, _ := goavro.NewCodec(` { "namespace": "TestSchema", "type": "record", "name": "TestData", "fields" : [ { "name" : "id", "type" : "int" }, { "name" : "value", "type" : "string" } ] }`) // 1-2.JSON文字列をGoデータへ変換 textual := []byte(`{"id":10001,"value":"test_value"}`) native, _, err := codec.NativeFromTextual(textual) // nativeはmap[string]interface{}型 if err != nil { panic(err) } log.Printf("textual length:[%d]\n", len(textual)) // 2.GoデータをAvroバイナリへシリアライズ binary, err := codec.BinaryFromNative(nil, native) // binaryは[]byte型 if err != nil { panic(err) } log.Printf("binary length:[%d]\n", len(binary))処理結果
元のJSON文字列33バイトから、バイナリデータ変換後は14バイトまで圧縮されています。
textual length:[33] binary length:[14]デシリアライズ処理手順
以下の手順で実行します。
Codecはシリアライズと同じものを使用できます。// 3.AvroバイナリデータをGoデータへデシリアライズ native, _, err := codec.NativeFromBinary(binary) if err != nil { panic(err) } log.Printf("Deserialized:%v\n", native) // 4.JSON文字列に戻すことも可能 textual, err := codec.TextualFromNative(nil, native) if err != nil { panic(err) } log.Printf("JSON string :%v\n", string(textual))処理結果
バイナリから復元されたデータはmap[string]interface{}型に変換されます。
Deserialized:map[id:10001 value:test_value] JSON string :{"value":"test_value","id":10001}どの辺がポイント?
・データ構造の情報を持たない
シリアライズにもデシリアライズにもスキーマ定義が必要で、その管理も必要になりますが、スキーマに書かれている情報を持つ必要が無いのでデータは小さくなります。
永続化にも通信にも有用です。
取り急ぎGolangでAvroのデータを扱いたい時は上記ソースコードをコピペして、スキーマ定義とJSON文字列を適切な値へ置換してください。(エラー処理の実装も忘れずに)
Appendix
- 投稿日:2020-11-25T09:03:53+09:00
WindowsでのGo開発環境構築
社内で、A Tour of Goを使ってGoの勉強会をしているので、学んだことをメモしておく。
まとめ記事はこちら。初回はWindowsでGoの開発環境を整える。
本当はA Tour of Go上で開発をしたかったが、Web上では補完効かなかったため、急遽vscodeでの開発に切り替えた。前提
- vscodeインストール済み
手順
- こちらから、Goをインストールする
- vscodeを起動
- Goをインストールする前にvscodeを起動していた場合、vscodeのterminalに環境変数が反映されないので、vscodeを再起動する必要がある
- vscodeにGoの拡張をインストール
- 適当にGoファイルを作成し、
A Tour of Go
のコードを書き写す- importしているfmtをクリックして定義元に飛ぼうとするが、必要なパッケージがインストールされていないので飛べない
- そうするとvscodeが察して、右下に
Godef
をインストールしますか? と表示される- 同じ要領でguru、Delve等もインストールできた
- この記事を参考し、デバッグもできた
所感
今日はここまで。
他の環境構築記事では、環境変数(GOPATH)の指定や各種ツールのインストールも自分で行っていた。
しかし、Goをインストールしたり、vscodeの案内に従っているだけでそれらの設定も自動で行ってくれるので、自分でやることは驚くほど少ない。感動。今日は環境構築で終わったので、明日から本格的に
A Tour of Go
を進めていく。
- 投稿日:2020-11-25T09:00:42+09:00
社内Go勉強会まとめ
はじめに
社内の有志で、Go言語をキャッチアップするため、1日30分位使ってA Tour of Goを進めてみた。
その時の内容を残しておく。A Tour of Go
#1
- 投稿日:2020-11-25T01:20:34+09:00
LeetCodeに毎日挑戦してみた26. Remove Duplicates from Sorted Array (Python、Go)
はじめに
無料英単語サイトE-tanを運営中の@ishishowです。
プログラマとしての能力を上げるために毎日leetcodeに取り組み、自分なりの解き方を挙げていきたいと思います。
Leetcodeとは
leetcode.com
ソフトウェア開発職のコーディング面接の練習といえばこれらしいです。
合計1500問以上のコーデイング問題が投稿されていて、実際の面接でも同じ問題が出されることは多いらしいとのことです。golang入門+アルゴリズム脳の強化のためにgoとPythonで解いていこうと思います。(Pythonは弱弱だが経験あり)
8問目(問題26)
26. Remove Duplicates from Sorted Array
- 問題内容(日本語訳)
並べ替えられた配列numsを指定して、各要素が1*回*だけ表示され、新しい長さを返すように、重複をインプレースで削除します。
別の配列に余分なスペースを割り当てないでください。O(1)の余分なメモリを使用して入力配列をインプレースで変更する必要があります。
明確化:
戻り値が整数であるのに、答えが配列である理由がわかりませんか?
入力配列は参照によって渡されることに注意してください。これは、入力配列への変更が呼び出し元にも認識されることを意味します。
内部的には、これについて考えることができます。
// numsは参照によって渡されます。(つまり、コピーを作成せずに) int len = removeDuplicates(nums); //関数内のnumsへの変更は、呼び出し元に認識されます。 //関数から返された長さを使用して、最初のlen要素を出力します。 for(int i = 0; i <len; i ++){ print(nums [i]); }Example 1:
Input: nums = [1,1,2] Output: 2, nums = [1,2] Explanation: Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn't matter what you leave beyond the returned length.Example 2:
Input: nums = [0,0,1,1,1,2,2,3,3,4] Output: 5, nums = [0,1,2,3,4] Explanation: Your function should return length = 5, with the first five elements of nums being modified to 0, 1, 2, 3, and 4 respectively. It doesn't matter what values are set beyond the returned length.ヒント1
この問題で焦点を当てる重要なポイントは、ソートされる入力配列です。重複する要素に関する限り、特定の配列が並べ替えられたときの配列内でのそれらの位置は何ですか?答えは上の画像を見てください。要素の1つの位置がわかっている場合、重複するすべての要素の位置もわかっていますか?
ヒント2
配列をインプレースで変更する必要があり、最終的な配列のサイズは入力配列のサイズよりも小さくなる可能性があります。したがって、ここでは2ポインターアプローチを使用する必要があります。1つは、元の配列の現在の要素を追跡し、もう1つは一意の要素のみを追跡します。
ヒント3
基本的に、要素が検出されたら、その重複をバイパスして、次の一意の要素に進む必要があります。
考え方
新規インデックスを作成 (nw_index)
len(nums)回ループを回して重複していたらそのまま進める
重複していなかったら新規インデックスの番号にその値を代入する
戻り値は長さを返すのでプラス1します
- 解答コード
class Solution(object): def removeDuplicates(self, nums): nw_index = 0 for i in range(len(nums)): if nums[nw_index] != nums[i]: nw_index +=1 nums[nw_index] = nums[i] return (nw_index + 1)
- Goでも書いてみます!
func removeDuplicates(nums []int) int { nw_index := 0 for _, num := range nums { if nums[nw_index] != num { nw_index++ nums[nw_index] = num } } return (nw_index + 1) }別解
def removeDuplicates(self, nums): nums[:] = sorted(set(nums)) return len(nums)手順説明
set() でset型(集合型)に変換します
set型とは?
set
型は重複しない要素(同じ値ではない要素、ユニークな要素)のコレクションで、和集合、積集合、差集合などの集合演算を行うことができる。setは集合なので順番がぐちゃぐちゃになりますのでsorted関数で昇順に戻します
nums[:]に参照を代入します。ここでnums=にしないのは新たにオブジェクトを生成してメモリを消費しないためです