fread()とfwrite()
バイナリデータを読み書きするには、fread() と fwrite() 関数を使う。これらはどんなデータでもバイナリ表現を使って読み書きができる。一般的な形式は次のとおり。
size_t fread(void *バッファ, size_t サイズ, size_t 数値, FILE *ストリーム);
size_t fwrite(void *バッファ, size_t サイズ, size_t 数値, FILE *ストリーム);
fread() は「ストリーム」から「サイズ」バイトの大きさのオブジェクトを「数値」個読み込んで、「バッファ」の指す領域に格納する。戻り値は実際に読み込んだオブジェクトの数。これが「数値」よりも小さければ、エラーが発生したか、ファイルの終わりに達したことになる。どちらかは feof() と ferror() で調べられる。
fwrite() は逆のことを行う。「バッファ」から「サイズ」バイトの大きさのオブジェクトを「数値」個、「ストリーム」に書き込む。戻り値は書き込んだオブジェクト数で、「数値」よりも小さければエラーが発生したということだ。
void型ポインタ
上の、「void *バッファ」というのは、void 型のポインタだ。void 型のポインタは、型変換を行わずに任意の型のデータを指すことのできるポインタで、汎用ポインタ(generic pointer)と呼ばれる。
fread() や fwrite() ではどのような型のデータを扱うかわからないので(違う言い方をすると、どんな型のデータでも扱えるように)、void 型のポインタを使っている。
size_t
size_t 型は stdio.h で定義されている型で、この型の変数はコンパイラがサポートする最大オブジェクトの大きさを持つ値を保持できる。基本の型ではなく size_t 型を使うのは、コンパイラがそれぞれの環境の違いを吸収できるようにするため。
sizof
sizeof 演算子は型、あるいは変数の大きさをバイト数で返す。
sizeof(型);
sizeof 変数;
sizeof を型に対して使うときにはカッコで囲む必要があるけど、変数に対して使うときにはカッコが囲んでも囲まなくてもいい。
例1
次のプログラムは、10 この要素(double 型)をバイナリでファイルに書き込み、開きなおして読み込んで、画面に表示する。当然だけど、ファイルを開くときはバイナリモードで開く必要がある。
#include
#include
double d[10] = {
10.23,
19.87,
1002.23,
12.9,
0.897,
11.45,
75.34,
0.0,
1.01,
875.875
};
int main(void)
{
int i;
FILE *fp;
/* write array data */
if ((fp = fopen("myfile", "wb")) == NULL) {
printf("Cannot open file.\n");
exit(1);
}
for (i = 0; i < 10; i++) {
if (fwrite(&d[i], sizeof(double), 1, fp) != 1) {
printf("Error: at writing.\n");
exit(1);
}
}
fclose(fp);
/* clear array */
for (i = 0; i < 10; i++) {
d[i] = -1.0;
}
/* read array data */
if ((fp = fopen("myfile", "rb")) == NULL) {
printf("Cannot open file.\n");
exit(1);
}
for (i = 0; i < 10; i++) {
if (fread(&d[i], sizeof(double), 1, fp) != 1) {
printf("Error: at reading.\n");
exit(1);
}
}
fclose(fp);
/* display array data */
for (i = 0; i < 10; i++) {
printf("%f\n", d[i]);
}
return 0;
}
takatoh@nightschool $ ./sample_9_5a
10.230000
19.870000
1002.230000
12.900000
0.897000
11.450000
75.340000
0.000000
1.010000
875.875000
ちなみに myfile ファイルを cat すると、
takatoh@nightschool $ cat myfile
�(\u$@��Q��3@�p=
�Q������)@NbX9��?fffff�&@�(\�R@)\�(�?_�@takatoh@nightschool $
となっている。バイナリだから人間には読めない。
例2
上の例では、配列の要素を1つずつ書き込み・読み込みしていたけど、配列はメモリ上に連続して確保されているので、1つの塊として1度で済ませることもできる。
#include
#include
double d[10] = {
10.23,
19.87,
1002.23,
12.9,
0.897,
11.45,
75.34,
0.0,
1.01,
875.875
};
int main(void)
{
int i;
FILE *fp;
/* write array data */
if ((fp = fopen("myfile", "wb")) == NULL) {
printf("Cannot open file.\n");
exit(1);
}
if (fwrite(d, sizeof d, 1, fp) != 1) {
printf("Error: at writing.\n");
exit(1);
}
fclose(fp);
/* clear array */
for (i = 0; i < 10; i++) {
d[i] = -1.0;
}
/* read array data */
if ((fp = fopen("myfile", "rb")) == NULL) {
printf("Cannot open file.\n");
exit(1);
}
if (fread(d, sizeof d, 1, fp) != 1) {
printf("Error: at reading.\n"); exit(1);
}
fclose(fp);
/* display array data */
for (i = 0; i < 10; i++) {
printf("%f\n", d[i]);
}
return 0;
}
30 行目で配列を一気に書き込み、49 行目では一気に読み込んでいる。結果は同じになる。
takatoh@nightschool $ ./sample_9_5b
10.230000
19.870000
1002.230000
12.900000
0.897000
11.450000
75.340000
0.000000
1.010000
875.875000