バイナリデータ
数値を入出力する
以前ファイル操作の基本を覚えました
fprintf()やfscanf()関数で文字データを操作することができました
しかし、以前扱った関数は整数型には不向きです
なぜならば、fprintf()関数などはバイナリデータを文字データに変換するからです
詳細はASCIIコードを学習すればわかりますが、その意味はまったく違うものになっています
ここではバイナリデータのファイルへの入出力を試みてみましょう
バイナリデータと文字データの変換に対して、通常は意識する必要はありません
しかし、その変換作業がアプリケーションの実行速度に深刻な問題を起こす場合など
直接バイナリデータを扱う必要がでてくるケースもあります
たとえば、次のようなプログラムの場合の問題点を考えましょう
#include <stdio.h>
#include <stdlib.h>
int main() {
int i;
FILE *fp;
printf("値を入力してください");
scanf("%d" , &i);
if ((fp = fopen("test.txt" , "w")) == NULL) {
fprintf(stderr , "ファイル操作中にエラーが発生");
exit(1);
}
fprintf(fp , "%d" , i);
fclose(fp);
return 0;
}
この場合は、バイナリではなくiの数値がASCIIコードに変換されて出力されます
65 ならば "65" と同じ意味になるということです
もちろんそれが目的ならば問題はありませんが、算術用として一時保存する場合は困ります
方法としてはfputc()やfgetc()を使うのもひとつの方法です
ですが、バイナリデータ専用としてより適した関数が用意されています
それがfwrite()関数です
size_t fwrite(void *バッファ , size_t サイズ , size_t カウント , FILE *ストリーム);
この関数はバッファからサイズ分のデータをカウント回ストリームに出力します
戻り値は、実際に書き込んだ回数を返します
void*型やsize_t型については、すでに説明しました
#include <stdio.h>
#include <stdlib.h>
int main() {
char i[] = { 65 , 66 , 67 , 68 , 69 , 70 };
fwrite(&i , sizeof(char) , 6 , stdout);
printf("\n");
fwrite(&i , sizeof(char)+1 , 3 ,stdout);
return 0;
}
このプログラムでは、fwrite()を使って配列のバイナリデータを標準出力に出力しています
標準出力やテキストエディタなどに出力すると、バイナリデータがASCIIコードになって確認できます
ABCDEF
ABCDEF
このような結果になったはずです
65はASCIIコードでAです
ここで、65と表示されずAと表示されたということは、データがバイナリデータで扱われたということです
fwrite()関数を二回使っていますが、やっている内容は同じです
1バイトを6回分まで出力するか、2バイトを3回分まで出力するかです
出力される合計バイト数は同じですね
しかし、このプログラムのchar型変数iの型をint型に変更してみると上の結果は得られません
バイナリデータでは、上位ビットが無視されないので
6バイトまでの出力ならば i[0] と i[1] の途中(上位2バイト)までしか出力されません(intが4バイトの場合)
次に、同様の方法でバイナリデータを読み込む方法を覚えましょう
バイナリデータをそのまま読み込むにはfread()関数を使用します
size_t fread(void *バッファ , size_t サイズ , size_t カウント , FILE *ストリーム);
引数や戻り値の内容はfwrite()とまったく同じです
ストリームからサイズ分をカウント個バッファに読み込みます
#include <stdio.h>
#include <stdlib.h>
int main() {
int i[] = { 100 , 200 , 300 } , re[3];
FILE *fp;
if ((fp = fopen("test.txt" , "w")) == NULL) {
printf("ファイル操作中にエラーが発生");
exit(1);
}
fwrite(i , sizeof(int) , 3 , fp);
fclose(fp);
if ((fp = fopen("test.txt" , "r")) == NULL) {
printf("ファイル操作中にエラーが発生");
exit(1);
}
fread(re , sizeof(int) , 3 , fp);
fclose(fp);
printf("%d" , re[0] + re[1] + re[2]);
return 0;
}
データはバイナリデータとして出力されます
このデータをバイナリデータとして読み込むことによって、そのまま計算式に使えます
作成されたファイルをメモ帳などのテキストエディタで開いても、意味不明な記号にしか見えません
これは、ASCIIコード(文字データ)ではなく、バイナリデータだからです
ASCIIコードとしてデコードしても、その多くは意味のない数列なので、結果として意味不明な記号が出てきます
このプログラムで確認したように、アプリケーションの設定データなど
読み込んで直接計算式に使用したりするデータなどはバイナリとして扱えます
とくに今回紹介した関数は、配列として扱うのに非常に優れています
実用化するには、戻り値を監視するなどして、エラー対策もするのが一般的です
size_t fwrite( const void*buffer, size_t size, size_t count, FILE *stream );
書式化せずにストリームにデータを書き込みます
ヘッダ - stdio.h
buffer - 書き込むデータのバッファを指定します
size - バイト単位で書き込む項目の単位を指定します
count - 書き込まれる項目数を指定します
stream - 出力するFILE構造体へのポインタを指定します
戻り値 - 実際に書き込んだ項目数を返します。エラーの場合はcount未満
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
書式化せずにストリームからデータを読み込みます
ヘッダ - stdio.h
buffer - 書き込むデータのバッファを指定します
size - バイト単位で書き込む項目の単位を指定します
count - 書き込まれる項目数を指定します
stream - 読みこむFILE構造体へのポインタを指定します
戻り値 - 実際に書き込んだ項目数を返します。エラーの場合はcount未満