Rでのバイナリファイルの扱い方
RでもFortranなど他のプログラム言語の書き出したバイナリファイルを読んでプロットしたり、画像や音声データのメタ情報を読んで統計解析をかけたりできます。
Rでデータを読み書きするときは、CSVのような構造化されたテキストファイルを使うときが多いと思いますが、Rにもバイナリ・データの操作のためにraw型と、そのための関数が用意されています。
raw型にはpack形式とunpack形式の使い方があります。numeric型やcharacter型とpack形式raw型は、相互に変換できます。integer型で表される数値文字参照(コード番号)から、pack形式もしくはunpack形式のraw型にできます。character型で表される16進数値文字参照(16進コード番号)から、pack形式もしくはunpack形式のraw型にできます。バイナリファイルはpack形式raw型の塊として取り扱いますが、numeric型やinteger型と看做して入力できます。numeric型やinteger型のベクトルをpack形式raw型の塊として書き込めば、バイナリファイルになります。本稿ではこれらのことを、実際に操作でなぞります。
Rは対話型インターフェイスなので、慣れてきたらバイナリ・エディタの代わりに使えるかも知れません。
1 raw型の扱い方
raw型ベクトルも他の型と同様にコピーや変更ができますが、やはり他の型との変換が肝になります。
1.1 pack形式raw型への変換
writeBin
でraw型に変換するのは意外かも知れません。
# character型はcharToRawで変換できる
<- charToRaw("a")) (p_raw_char
[1] 61
# integer型はas.rawで変換できる/数値文字参照(コード番号)入力
<- as.raw(123)) # 123の代わりに0x7bと書いてもよい (p_raw_int
[1] 7b
# character型で表される16進数値文字参照(16進コード番号)
<- as.raw(as.hexmode("7b"))) (p_raw_int_hex
[1] 7b
# numeric型に限らずwriteBinはどの型も変換できる
<- writeBin(c(1.23, 4.56), raw())) (p_raw_numeric
[1] ae 47 e1 7a 14 ae f3 3f 3d 0a d7 a3 70 3d 12 40
1.2 pack形式raw型からの変換
readBin
でraw型から戻せるのも以外かも知れません。
# character型にはcharToRawで変換
rawToChar(p_raw_char)
[1] "a"
# integer型の数値文字参照(コード番号)にはas.integer
as.integer(p_raw_int)
[1] 123
# integer型にしてから16進数表記にする
sprintf("%x", as.integer(p_raw_int_hex))
[1] "7b"
# 型と長さnの指定が必要
readBin(p_raw_numeric, numeric(), n = 2)
[1] 1.23 4.56
1.3 unpack形式への変換
アセンブラ使いだった人以外、使い道が思いつかないかも知れませんが、0と1の配列にできます。
# 数値文字参照(コード番号)からの変換
<- intToBits(123)) (up_raw_int
[1] 01 01 00 01 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[26] 00 00 00 00 00 00 00
# pack形式raw型からの変換
rawToBits(p_raw_int)
[1] 01 01 00 01 01 01 01 00
長さが異なるのは、integer型は32bitsで、raw型は8bitsだからです。
1.4 unpack形式からの変換
0と1の配列から、まとめたものにもできます。
packBits(up_raw_int, "raw") # raw型にする
[1] 7b 00 00 00
packBits(up_raw_int, "integer") # integerとdouble(numeric)は直接戻せる
[1] 123
2 バイナリファイルの扱い方
エラーは起きないと見なせて、ファイルフォーマットが決まっていて、データをすべてまとめて読み書きできるのであれば、煩雑なことは無いです。
2.1 書き出し
numeric型の乱数列をつくってファイルに書き出します。
<- 10
n <- "vector.dat"
fname <- round(runif(n, min=0.5, max=9.5), 2)) (v
[1] 0.93 2.28 5.82 5.74 8.00 7.52 5.03 2.10 3.93 9.02
<- file(fname, "wb")
os writeBin(v, os)
close(os)
2.2 読み込み
つくったバイナリ・ファイルを読み込んでみましょう。
n <- 10
としてもよいのですが、ファイルサイズからベクトルに含まれるnumeric
型の値の個数を計算します。
<- "vector.dat"
fname <- file.info(fname)[, "size"]
fs <- length(writeBin(numeric(1), raw())) # Cのsizeofの同等物が無い
size_of_numeric <- fs / size_of_numeric n
ファイルを開いてn
のnumeric
型を読み込みます。
<- file(fname, "rb", blocking = FALSE)
is <- readBin(is, numeric(), n = n)) # 変数の型と読み込む個数を指定する (v2
[1] 0.93 2.28 5.82 5.74 8.00 7.52 5.03 2.10 3.93 9.02
close(is)
書き出したv
と読み込んだv2
が同じか確認します。
if(all(v = v2)) print("v2 is the same as v.") else print("v2 is diffrent from v.")
Warning in all(v = v2): 'double' 型の引数を論理型に変換します
[1] "v2 is the same as v."
無事、同じでしたね。
3 逐次読み出しとランダムアクセス
Rのファイル入出力はC標準のファイル入出力と大きな差異はないです。一気に全体を読まない場合は、C言語のfeof
に該当するisIncomplete
がTRUE
になるまで繰り返し処理するコードを書きます。また、ランダムアクセス可能かはisSeekable
で確認でき、可能な場合はseek
やtruncate
ができます。一方で、flock
は標準では提供されません。
4 Fortranで書いたバイナリファイルを読む
数値解析の速度は申し分がないFortranですが、プロットなどのI/O周りの使い勝手は悪いです。Fortranの計算結果をファイルに書き出して、Rに読ますのもひとつの方法です。簡単な例でやり方を確認しておきましょう。
4.1 Fortranのコード
3行3列の行列をFortranで作ってバイナリ・ファイルに保存します。
program makeBin
implicit none
double precision, dimension(3, 3) :: m = reshape((/1,2,3,4,5,6,7,8,9/),(/3,3/))
open(17,file='data_seq.dat', form='unformatted', status='replace')
write(17) m
close(17)
open(17,file='data_str.dat', form='unformatted', access = "stream", status='replace')
write(17) m
close(17)
end program makeBin
2つファイルを作っていますが、オプションで形式が変わるからです。Fortranのバイナリー形式(unformatted
)のデフォルト(access = "sequential"
)のファイル書き出しだと、write
の度に前後にそれぞれ4バイトを足してきます1。Fortran
2003以降で使えるaccess = "stream"
ではつきません。
4.2 Rのコード
access = "sequential"
のdata_seq.dat
を読むコードを示します。
<- c(3, 3)
size <- "data_seq.dat"
fname <- file.info(fname)[, "size"]
fs <- file(fname, "rb", blocking = FALSE)
is <- readBin(is, "raw", fs)
buf # 先頭と末尾の4バイトを除外する
# access = "stream"で書いたならば不要
<- buf[-c(1:4, (fs - 3):fs)]
buf # raw型からnumeric型に変更
<- readBin(buf2, numeric(), n = prod(size));
v close(is)
<- matrix(v, size[1], size[2]) m
5 Cで書いたバイナリ・ファイルを読む
Fortranのaccess = "sequential"
のようにヘッダーとフッターをつけては来ませんが、C言語の二次元配列は行ごとに続けて値が入るので、Rのmatrix
で行列に戻すときにはbyrow=TRUE
が引数に要ります。
6 応用時の注意
Rに限った話ではありませんが、応用上はバイナリ・ファイルの構造の把握が大事です。昔の非Intelのシステムで読み書きするバイナリファイルでは、エンディアンが今のシステムの逆の場合もあります。readBin
とwriteBin
でendian
を引数に取れますが、big
を指定することになります。
この頭と尻尾の4バイトが何になるかはFortranコンパイラごとに異なり、gfortranだと中身のファイルサイズになります。ただし、gfortranで
write
したsequential
のバイナリーファイルの頭と尻尾を書き換えて、gfortranにread
させても問題なく読み取れます。実際のところ、何の機能も果たしていないようです。↩︎