自分がメンテナンスしている昔からあるperlスクリプトがデータの料が増えて処理にかなり時間がかかって体感的に遅いと感じるようになってきた。
そのスクリプトは標準ライブラリ程度にしか依存していなかったのでGoで書きなおしてみようかと思ってやってみた。
扱うデータのヘッダー部分が16進数の固定長データでperlではunpackして解析していた。 Goでunpackってどうやるの?ってなってその時の対応の備忘録。
Perlのpack/unpackについてはこの記事を参照。
perlpacktut - pack と unpack のチュートリアル - perldoc.jp
while (<>) {
my($date, $desc, $income, $expend) = unpack("A10xA27xA7A*", $_);
...
}
Goで16進数の固定長データをどう扱ったかというと基本は strconv.ParseUintと hex.DecodeStringだけで問題なかった。
先頭からN byteまとめて10進数に変換するならこう。
s := "ABCDEF"
ui, _ := strconv.ParseUint(s[:2], 16, 0)
fmt.Printf("%#V\n", ui) // %!V(uint64=2748)
逆に、2byteずつ変換するならこんな感じ。
s := "ABCDEF"
bytea, _ := hex.DecodeString(s[0:6])
fmt.Printf("%#V\n", bytea) // %!V([]uint8=[171 205 239])
これで16進数の固定長のデータが大体変換できるだろう。
strconv.ParseUint の第2引数で8進数でも2進数でも変換は可能となる万能な関数。
個人的に16進数で日付データとか持たせていてそれをデコードして出来たuint8のbyte配列に対してmapで文字列に変換したいと思ったんだけどいい実装が無かったので泥臭くPrintfで処理した。
mapのような関数型なプログラミングが恋しい。
package main
import "fmt"
import "strconv"
import "encoding/hex"
func main() {
s := "ABCDEF"
// Nbyteまとめて変換するならこう
bytea, _ := hex.DecodeString(s[0:6])
fmt.Printf("%#V\n", bytea)
// 先頭から2byteずつ変換するならこう
ui, _ := strconv.ParseUint(s[:2], 16, 0)
fmt.Printf("%#V\n", ui)
}