バイナリデータを読み込むには

VBAではファイルを扱う場合はFileSystemObjectクラスを利用することが一般的ですが、バイナリファイルに関してはFileSystemObjectクラスでは扱えません。そのため、Openステートメントでバイナリファイルを開きます

具体的な書き方は後述のサンプルコードになりますが、概要だけを書くとOpenステートメントを実行する際のMode引数でバイナリファイルであることを指定します。

バイナリデータを扱う場合は、だいたい以下の3パターンのいずれかでの読み込み方法になります。

  • 全データを1度に読み込む方法
  • 1バイトずつ読み込む方法
  • 一定のサイズ(例えば500バイト)ごと読み込む方法

それぞれ少し書き方が異なる点があり、バイナリファイルを扱うときに一番ハマるのが「データ取得の書き方」なので、それぞれについて説明します。

なお、Openステートメントなどの詳細については「VBAでテキストファイルの読み書きを行う」をご参照ください。

バイナリファイルデータを1度に全て読み込む場合

全データを1度に読み込む方法は一番簡単な方法です。バイナリファイルのサイズが1KB程度などのように小さいことが事前に分かっている場合は、この方法をオススメします。

ポイントは「Byte配列 = InputB(ファイルサイズ, ファイル番号)」の部分です。ここで全データを配列にセットしています。

Byte型配列に格納したあとは必要な処理を行います。ここではくるくる回して1バイトずつ出力しているだけですが、実際のバイナリデータを扱う場合はByte型の16進数値がどうなのでこういうことをやる、という処理になることが多いと思います。

1バイトのByte型データの判定方法の書き方として改行コードの場合のサンプルを書いてます。

バイナリファイルデータを1バイトずつ読み込む場合

1バイトずつ判定して処理を行いたい場合はGetステートメントかInputB関数を使ってファイル終端まで1バイトずつ読み込むコードを書きます。

以下のコードはGetステートメントとInputB関数の両方を使って同じバイト位置を1バイトずつ取得して出力しています。もちろん実際に使う場合はGetかInputBのどちらか一方のみで構いません。

バイナリデータを取得する際にGetステートメントとInputB関数のどちらを使ってもいいのですが、当然違いがあるため用途に合わせて使い分けをした方がいいでしょう。

Getステートメントは指定したファイルデータの位置(Seek関数での位置)から変数の領域分だけ読み取ります。言い方を変えると、変数に入る分だけ読み取ります。

InputB関数は第一引数で指定したサイズ分をバイナリデータのまま文字列として読み込みます。バイナリデータのまま、というのは似た関数にInput関数がありますが、こちらはUnicodeに変換した文字列が変数に格納されますが、InputB関数はなにも変換せず変数に文字列として格納します。

GetステートメントとInputB関数のいずれの場合も、可変サイズの取得を行うときは、ファイル終端を超えないように考慮が必要になります。

ちなみに私はバイナリデータを1バイトずつ取得するのであればGetステートメントを使うことが多いです。理由は、バイナリデータを扱うのであればByte型を使いたいところですが、InputB関数は文字列型(String型)を返すため感覚的に嫌、というのがあります。これは好みとしか言いようがないかもしれません。

バイナリファイルデータを一定サイズごとに読み込む場合

一番コードとして複雑になるのがここで紹介する一定サイズごとに読み込む場合です。どうしてもループ終端に達するかどうかの判定や読み込みサイズの調整が必要になります。

やらなくていいならこの方法はしない方がいいですが、バイナリファイルのサイズが大きい場合やサイズが不明の場合はこの方法しかない場合があります。

ここではGetステートメントでの書き方を紹介します。もちろんInputB関数でもコーディング可能です。

ここでは21バイト(20 + 1)を配列のサイズとしてByte型配列を用意し、それをバイナリファイルのEOFに達するまで21バイトずつ取得していくコードです。

例えばバイナリファイルのサイズが150バイトだとした場合、ループごとで21、42、63、84、105、126、147、と読み込んでいきますが、終端の直前では150バイトから読み込み済みの147バイトを引いた3バイト+終端の4バイトが読み込むサイズになります。なお、終端はNULL(0x00)が1バイト読み込まれます。

そのため、ループの最後の部分で読み込みサイズが終端の直前かどうかの判定を行い、最後に読み込む配列サイズをRedimで調整しています。

ちなみに調整しなくても21バイトずつ取得しても終端に達するとループは抜けますが、その時の配列には終端の0x00以降の要素には全て0x00が設定されます。扱っているバイナリデータに0x00がないことが分かっているのであれば終端直前のRedimは不要ですが、その後で1バイトずつ処理することをあったり、0x00がデータとして存在している可能性があるなら配列のサイズ調整はしておいた方がよいでしょう。