※Swift4です
Swiftでバイナリデータを扱う
SwiftではバイナリデータをData型で扱うことができます。
let data = Data(...)
data[0] // -> UInt8
data[1] // -> UInt8
...
Int8
は1バイトのデータのことであるため、data配列をいい感じに操作すればバイナリデータを扱うことができそうです。
structで扱いたい
たとえば、バイナリデータが |type 2byte|data 4byte|type 2byte|data 4byte|...
というデータの並びになっているとき、
struct SingleData {
let type: UInt16
let data: UInt32
}
上↑のようなstructを用意して、読み込んだ後に配列に変換してから操作すると楽そうです。
var isEnd = false
var counter = 0;
var datas: [SingleData] = []
while isEnd {
let type: UInt16 =
UInt16(data[counter * 0]) << 8 +
UInt16(data[counter * 1])
let data: UInt32 =
UInt32(data[counter * 2]) << 8 * 3 +
UInt32(data[counter * 3]) << 8 * 2 +
UInt32(data[counter * 4]) << 8 * 1 +
UInt32(data[counter * 5]) << 8 * 0
datas.append(SingleData(type: type, data: data))
}
... // 以下、datasを好きに使う
...けっこう大変ですね。
C言語だと、「共用体」と呼ばれる複数の型として操作できる型を使うことでバイナリデータをわりと簡単にstructとして扱うことができましたが、Swiftではどうなのでしょうか。
Swiftでバイナリデータをstructとして扱う
答えは、「withUnsafeBytes
を使う。」です。これによってそれなりに簡単にstructに変換することができるようになります。
// 値を return すると withUnsafeBytes の戻り値となる
let results = data.withUnsafeBytes({(ptr: UnsafePointer<SingleData>) -> [SingleData] in
let count = data.count / MemoryLayout<SingleData>.size // データの個数を計算
var tmp = ptr // ptr を var にしたい
var results: [SingleData] = []
for i in 0..<count {
// ptr.pointee で中身にアクセスできる。 *ptr みたいなもの?
results.append(ptr.pointee)
// ポインタを隣に移動する。 ptr++ みたいなもの?
ptr.successor()
}
})
... // results を好きに操作する
注意
structのプロパティにはlittle endianでアクセスします。
したがって、読み込むデータ(2byte以上)によってはbig endianでアクセスする必要があります。
type | data |
---|---|
0x0001 |
0x12345678 |
0x0002 |
0x12345678 |
0x0003 |
0x12345678 |
上↑のようなデータだったとき、results[0].type == 256(0x0100)
となってしまいます。
もし、データがbig endianで格納されているのであれば、bigEndian
プロパティを使ってアクセスしましょう。
こうです。
print(results[0].type.bigEndian)
JPEGを読み込むプログラムを書こうと思ったのですが、big endianでアクセスするのがめっちゃ面倒だったので、諦めました。
以上です。よろしくお願いいたします。