読者です 読者をやめる 読者になる 読者になる

uokadaの見逃し三振は嫌いです

ここで述べられていることは私の個人的な意見に基づくものであり、私が所属する組織には一切の関係はありません。

Goで固定長データを取り扱った話

自分がメンテナンスしている昔からあるperlスクリプトがデータ量が増えて処理にかなり時間がかかって体感的に遅いと感じるようになってきた。
そのスクリプトは標準ライブラリ程度にしか依存していなかったのでGoで書きなおしてみようかと思ってやってみた。

扱うデータのヘッダー部分が16進数の固定長データでperlではunpackして解析していた。 Goでunpackってどうやるの?ってなってその時の対応の備忘録。

Perlのpack/unpackについてはこの記事を参照。 perlpacktut - pack と unpack のチュートリアル - perldoc.jp

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)

}

http://play.golang.org/p/OlI5FIMvct

基礎からわかる Go言語

基礎からわかる Go言語