『やさしいC』バイナリファイルとランダムアクセス

C

バイナリファイルに書き込む

  さて、ここまで読み書きをしてきたファイルは、全てテキストで読み書きができるファイルです。このようなファイルのことを、テキストファイル(text file)といいます。
  テキストファイルは人間が読み書きを行えるために、大変便利なのですが、内容によっては、ファイルサイズが大きくなったり、処理速度が遅くなるなどといったこともあります。
  このような時、バイナリファイル(binary file)と呼ばれるファイルを扱うと便利です。バイナリファイルは、コンピューターの内部でデータ形式のまま保存されたファイルです。
  バイナリファイルを扱うことで扱うこともできますので、早速このファイルを操作するコードを作成してみることにしましょう。

#include <stdio.h>
#define NUM 5

int sample13()
{
	FILE* fp;
	int test[NUM] = { 80, 60 , 22, 50, 75 };
	int i;

	fp = fopen("test1.bin", "wb"); // バイナリファイルを書き込み用にオープンする

	if (fp == NULL)
	{
		//オープンできないときのエラー処理
		printf("ファイルをオープンできませんでした。\n");
		return 1;
	}
	else
	{
		printf("ファイルをオープンしました。\n");

	}

	for (i = 0; i < NUM; i++)
	{
		fwrite(&test[i], sizeof(int), 1, fp); //配列の各要素をファイルに出力します
	}
	printf("ファイルに書き込みました。\n");

	fclose(fp); //ファイルをクローズする
	printf("ファイルをクローズしました。\n");

	return 0;
}
実行結果:

ファイルをオープンしました。
ファイルに書き込みました。
ファイルをクローズしました。

  これで、test1.binというバイナリファイルが作成されました。
  バイナリファイルを扱う方法も、これまでの操作とほとんど同じです。バイナリファイルを扱うには、ファイルをオープンするときにオープンモードとしてを”b”を追加します。ここでは書き込み用にオープンするので、wとと組み合わせて”wb”と指定しています。

fp = fopen("test1.bin", "wb"); // バイナリファイルを書き込み用にオープンする

  このバイナリファイルには、fwrite()関数を使って書き込みをすることができます。この関数は、書き込む要素へのポインタ(要素のアドレス)と、書き込むデータサイズを指定します。ここでは配列の要素を1つずつ書き込むことにしますので、次のように書き込みを行っているわけです。

for (i = 0; i < NUM; i++)
{
	fwrite(&test[i], sizeof(int), 1, fp);
}
&test[i]は書き込む要素へのポインタです
sizeof(int)は書き込むデータサイズです。
1は書き込む個数です。

  なお、配列の場合は繰り返し文を使って、1つずつ書き込まずに、一度に次のように書き込むこともできますので、覚えておきましょう。

fwrite()関数の構文

fwrite(書き込むデータへのポインタ」, データサイズ, 個数, ファイルポインタ);

重要

指定サイズのデータを書き込むには、fwrite()関数を使う。

バイナリファイルを読み込む

  さて、プログラムを実行し、作成されたtest1.binをテキストエディタで開くと、これまでと違って、読めないデータとなっています。
  しかし、このバイナリファイルから、さきほど書き込んだデータを読み込んで画面に出力することができます。今度は次のようなコードを作成してみてください。

#define NUM 5
int sample14()
{
	FILE* fp;
	int test[NUM];
	int i,j;

	fp = fopen("test1.bin", "rb"); // バイナリファイルを読み込み用にオープンする

	if (fp == NULL)
	{
		//オープンできないときのエラー処理
		printf("ファイルをオープンできませんでした。\n");
		return 1;
	}
	else
	{
		printf("ファイルをオープンしました。\n");

	}

	for (i = 0; i < NUM; i++)
	{
		fread(&test[i], sizeof(int), 1, fp); //配列の各要素にファイルから読み込みます
	}
	for (j=0; j < NUM; j++)
	{
		printf("%d\n", test[j]);
	}

	fclose(fp); //ファイルをクローズする
	printf("ファイルをクローズしました。\n");

	return 0;
}
実行結果:

ファイルをオープンしました。
80
60
22
50
75
ファイルをクローズしました。

  今度は、バイナリファイルに書き込まれているデータを読み込んで、画面に出力しています。
  バイナリファイルを読み込むときにはオープンモードとして”rb”と指定します。データの読み込みにはfread()関数を使います。さきほどの書き込みと似ていますね。

for (i = 0; i < NUM; i++)
{
	fread(&test[i], sizeof(int), 1, fp); //配列の各要素にファイルから読み込みます
}
&test[i]は読み込む要素へのポインタです
sizeof(int)は読み込むデータサイズです。
1は読み込む個数です。

  こちらも繰り返し文を使わずに、次のように読み込むことができます。

fread(test, sizeof(test), 1, fp);

  バイナリファイルから読み込んだデータは配列test[]に格納されますので、この値を出力しているわけです。

fread()関数の構文

fread(読み込む領域へのポインタ, データサイズ, 個数, ファイルポインタ);

重要

指定サイズのデータを読み込むには、fread()関数を使う。

ランダムアクセスをする

  では次に、ファイルへのアクセス方法について学ぶことにしましょう。これまでのファイルでは、ファイルの先頭から順番にデータを読み書きをするものとなっています。このように先頭から順にファイルを扱う方法を、シーケンスアクセス(sequential access)といいます。
  これに対してファイルの途中にアクセスする方法を、ランダムアクセス(random access)といいます。このアクセス方法によって効率の良いプログラムを作成することができる場合がありますので、覚えておくことにしましょう。
  ランダムアクセスをするには、「ファイル中のどの位置からデータを読み書きするのか」、と言う指定をする事が必要です。このためには、データを読み書きしている「現在位置」の概念が必要になります。この位置はファイルポジションとも呼ばれています。

78256369
↑ファイルの先頭から順次読み書きする方法を「シーケンスアクセス」といいます。

78256369
  ↑ファイルの任意の場所から読み書きする方法を「ランダムアクセス」といいます。 

  では、ランダムアクセスを行うコードを作成してみます。ここでは、先ほど作成したバイナリファイルのを使ってみましょう。このファイルについては、指定された部分のデータだけを読み込んでみることにします。

#define NUM 5
int sample15()
{
	FILE* fp;
	int num;
	int i;

	fp = fopen("test1.bin", "rb"); // バイナリファイルを読み込み用にオープンする

	if (fp == NULL)
	{
		//オープンできないときのエラー処理
		printf("ファイルをオープンできませんでした。\n");
		return 1;
	}
	else
	{
		printf("ファイルをオープンしました。\n");

	}

	printf("何番目のデータを読み込みますか?(1~5)\n");
	scanf("%d",&i);

	fseek(fp,(i-1)*sizeof(int), SEEK_SET);  //読み込んだデータ位置に移動します
	fread(&num, sizeof(int), 1, fp);
	printf("%d番目のデータは%dです。\n", i, num);

	fclose(fp); //ファイルをクローズする
	printf("ファイルをクローズしました。\n");

	return 0;
}
実行結果:

ファイルをオープンしました。
何番目のデータを読み込みますか?(1~5)
3
3番目のデータは22です。
ファイルをクローズしました。

  ランダムアクセスをするには、fseek()関数を使って、アクセスするデータの位置に「現在位置」を移動します。

fseek(fp,(i-1)*sizeof(int), SEEK_SET);  //読み込んだデータ位置に移動します

  この関数は、3番目の引数で指定した開始位置から、2番目に指定したサイズ文分だけ移動する指定になっています。開始位置には次の3つの定義ずみのマクロを使用できます。

SEEK_SETファイルの先頭
SEEK_CUR現在位置
SEEK_ENDファイルの末尾

fseek()関数の構文

fseek(ファイルポインタ, 移動するサイズ, 開始位置)

  移動したら、その位置からint型のデータを1つ読み取るので、次のようにfread()関数を使って読み込みます。

fread(&num, sizeof(int), 1, fp);

  このようなコードを作成することによって、キーボードから指定した位置のデータを読み込むことができるようになっています。

重要

ファイルの先頭からシーケンスシャルアクセスができる。
ファイルの特定の箇所へのランダムアクセスができる。

コメント

タイトルとURLをコピーしました