C言語/ファイル入出力

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

ファイルのオープン[編集]

まず、ファイルを作成したり読書きしたりするための関数を使うには、 ヘッダー<stdio.h>をインクルードする。

#include <stdio.h>

int main() {
  FILE *fp = fopen("開きたいファイル名.拡張子", "モード");

  if (fp == NULL) {
    perror("開きたいファイル名.拡張子 を開けませんでした。");
    return 1;
  }

  // ここにファイルの操作内容(作成、読書きなど)を記述。

  fclose(fp);
}
ファイルに読書きなどの操作するためには、まず、そのファイルをオープンする(開く)必要がある。
オープンされていない状態のファイルは、操作できない。
fopenがファイル操作のためにオープンする関数で、FILE型へのポインタを返すので、変数 fp に保持する。
FILE型へのポインタの型は、
FILE *
です。

Visual Cで標準入出力関数などを使用する場合[編集]

Visual Cでは、標準設定ではfopen関数などの標準入出力関数の使用が禁止されており、そのまま上記のコードをコンパイルすると、

重大度レベル コード 説明 プロジェクト ファイル 行 抑制状態
エラーC4996’fopen’: This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

のような警告を表示し、コンパイルが完了しない。

このような禁止されている関数の使用を許可するために、#pragma warning(disable:4996)を加え、コンパイラの警告を無効化する(メッセージ通り fopen を fopen_s で置換えても良いが、fopen_s を用意している処理系は稀で、GCCもclangもサポートしておらず、環境を選ばずコンパイルするためには #ifdef _MSC_VER などでプリプロセッサーで条件コンパイルする方法があるが、コードの見通しが悪くなる上バッファサイズの計算のケアなどを見落としがちなので、警告を無効化することを選択した)。

Windowsでfopenを使う場合
#include "stdafx.h"
#include <stdio.h>

#pragma warning(disable:4996)

int main() {
  FILE *fp = fopen("開きたいファイル名.拡張子", "モード");

  if (fp == NULL) {
    perror("開きたいファイル名.拡張子 を開けませんでした。");
    return 1;
  }

  // ここにファイルの操作内容(作成、読書きなど)を記述。

  fclose(fp);
}

#pragma warning(disable:4996) が、fopenなどの関数の使用を許可するプラグマです。これは、stdafx.h など一連のWindows用ヘッダのインクルードの後に指令する必要がある。

Visual Studio で、デバッグ セッションの終了時にコンソールが閉じてしまう場合

Visual Studio で、コンソール アプリケーションのデバッグ セッションの終了時にコンソールが閉じてしまう場合は、

[ツール] -> [オプション] -> [デバッグ] -> [デバッグの停止時に自動的にコンソールを閉じる]

を無効にします。


Windows用のファイル操作のコード例:
#include "stdafx.h"
#include <stdio.h>

#pragma warning(disable : 4996)

int main() {
  FILE *fp = fopen("test1.txt", "w");

  if (fp == NULL) {
    perror("test1.txt を開けませんでした。");
    return 1;
  }

  fclose(fp);
}

fopen は、モードを "w" で開いた場合、もし対象のファイルが存在しないときは、その名のファイルを新規作成する。

なお、fopenのモードは、

書き込みモードの"w"と、
読み込みモードの"r"と、
追記モードの"a"

がある。

なお、一般にファイル操作のプログラミングでは、ファイルのオープンに失敗した場合を想定して、そのような処理を書く必要がある。

fclose は、ファイルを閉じる関数であり、書き込みモードであればバッファキャッシュをクリアしてから、読み出しモードであればその場でシステムにファイルをディスクリプターを返却(closeシステムコール)する。

読者は、ソースファイルのあるフォルダを確認してみて、"test1.txt"というファイルが作られている事を確認しよう。

"stdafx.h"って何?
プログラムの冒頭で#include "stdafx.h""stdafx.h" なるヘッダーをインクルードしています。

これはC言語標準にはなく、Windows(Visual Studio)固有のプリコンパイルヘッダーです。stdafx は Standard Application Frameworks の略で、MFC(Microsoft Foundation Class)の使用を想定しているものでした。 VS2019からが、"stdafx.h" から "pch.h"(Pre compile header)と機能と一致した名前になりました。

いずれにしても、Windows/Visual Studio でプリコンパイルヘッダーを使う状況でしか意味がありませんし、この本で扱うサイズのプリグラムではプリコンパイルヘッダーを使うメリットはありません。


読み書き[編集]

ファイルへの書き込み[編集]

ファイルを作成したりオープンしただけでは、まだ何も読み込まれないし、何も書き込まれない。

ファイルに何かデータを書き込むには、 fprintf という関数を使う。

fprintf の冒頭のfは、ファイル関係であることをあらわしている。最後のfは、もともとC言語には「printf」という関数があり、(最後のfは)Format の f。


さて、windowsの設定では、ある設定の場合では fopen と同様に fprintf が初期状態では使えない場合があるので、

#pragma warning(disable:4996)

で指令しておく必要の生じる場合もある。


インクルードすべきヘッダについては、標準出力のヘッダにあるので、

#include <stdio.h>

でインクルードすれば充分です。


さて fprintf で書き込むデータの形式は、変数でもいいし、書き込みたいテキストを直接指定することもできる。

また、もし 変数を書き込む場合なら、変数の型は、文字列型でもいいし、数値型でもいい。


ただし、書き込まれた先のテキストファイルでは、書かれたテキストの由来が何かは、もはや不明になる。つまり、書きこまれた先のテキストファイルでは、書かれた文字列は、すべて単なるテキスト文字の集まりとして扱われる。


さて、たとえばファイルに「test」とだけ書き込みたいなら、 fprintf(fp, "test \n"); というふうに入力すればいい。


何か変数をファイルに書き込みたい場合、たとえば整数型の変数だとして、変数名が「var」なら、それを書き込むには、あらかじめソースコードで

int var;

のように、どこかで宣言しておく。

そのあと、プログラム中にて変数「var」に目的の値を入力させるようにしておく。

そのあと、プログラムがコード内でのファイル書き込み実行場所にて

fprintf(fp, "%d \n", var);

のようなコードを実行するように、ソースコードを記述すればいい。



ファイル書き込むのできる変数の型は、数値型も文字列型でも可能です。


次のコード例として、文字列型の変数をファイルに書き込むコード例を示す。

コード例
#include "stdafx.h"
#include <stdio.h>

#pragma warning(disable:4996)


int main()
{
	FILE *fp = fopen("test1.txt", "w");
	if (fp == NULL) {
		perror("test1.txt を開けませんでした。\n");
		return 1;
	}
	printf("test1.txt をオープンしました。\n");

	char string[50];
	printf("キーボードから文字列を入力してください。\n");
    if (scanf("%s", &string) == EOF) {
        perror("");
        return 1;
   }
	printf("入力された文字列は %s です。 \n", string);

	printf("これをファイルに書き込みます。 \n");

	fprintf(fp, "%s \n", string);

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

このようにして、ファイルへの出力が出来る。


ファイルの読み取り[編集]

次に、ファイルからの読み取りをする方法を学ぼう。

ファイルからの読み取りには、まずfopenの際にモードを"r"にする。(英語で、本や文章などを読むことを read (リード)という。その read リードの頭文字 r のこと。)

読み取りの際の関数には fgetsfscanf を使えばいい。

fscanffgetsは、ファイルから1行ずつ、読み取ることができる。

なお、 fscanffgets の違いは、

行末の改行を読み込むかどうか、
空白文字(半角スペース)があると読み込みを中断するかどうか、

です。fscanfのほうが、空白文字(半角スペース)があると読み込みを中断します。 くわしくは、あとの章で説明します。

とりあえず、ファイル読み取りの具体例を調べていきましょう。

fscanf の例

あらかじめ、テキストファイル test1.txt に、なんらかの文字列を書いておこう。

一例として、

aaaaaaaaaaa
ssssssss

dddddd

と書いたとする。


コード例を示す。

#include "stdafx.h"
#include <stdio.h>

#pragma warning(disable:4996)


int main()
{
	FILE *fp = fopen("test1.txt", "r");
	if (fp == NULL) { // ここを読み取りモード"r"にするのを忘れないように
		perror("ファイルを開けませんでした。\n");
		return 1;
	}
	else {
		printf("ファイルをオープンしました。\n");
	}

	
	char str1[150];
	printf("文字列を読み取っています。\n");
	
    if (fscanf(fp, "%s", str1) == EOF) {
        perror("");
        return 1;
   }
    printf("ファイルに書いてある文字列\n");
	printf("%s\n", str1);

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


実行結果
ファイルをオープンしました。
文字列を読み取っています。
ファイルに書いてある文字列
aaaaaaaaaaa
ファイルをクローズしました。
続行するには何かキーを押してください . . .


このように、最初の1行(aaaaaaaaaaa)だけが読み取りされている。


2行目以降も読み取りたい場合には、その行数だけ、下記のように繰り返しfscanfを使う必要がある。

#include "stdafx.h"
#include <stdio.h>

#pragma warning(disable:4996)


int main()
{
	FILE *fp = fopen("test1.txt", "r"); // ここを読み取りモード"r"にするのを忘れないように
	if (fp  == NULL) {
		perror("ファイルを開けませんでした。\n");
		return 1;
	}
	else {
		printf("ファイルをオープンしました。\n");
	}

	char str1[150];
	printf("文字列を読み取っています。\n");

    if (fscanf(fp, "%s", str1) == EOF) {
        perror("");
        return 1;
    }
	printf("ファイル1行目に書いてある文字列\n");
	printf("%s\n", str1);


    if (fscanf(fp, "%s", str1) == EOF) {
        perror("");
        return 1;
    }
	printf("ファイル2行目に書いてある文字列\n");
	printf("%s\n", str1);

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


実行結果
ファイルをオープンしました。
文字列を読み取っています。
ファイル1行目に書いてある文字列
aaaaaaaaaaa
ファイル2行目に書いてある文字列
ssssssss
ファイルをクローズしました。
続行するには何かキーを押してください . . .

fgetsとfscanfの違い[編集]

fscanffgets の違いは、大まかには

行末の改行を読み込むかどうか、
空白文字(半角スペース)があると読み込みを中断するかどうか、

です。fscanfのほうが、空白文字(半角スペース)があると読み込みを中断します。fgetsのほうが、改行を読み込みます。

論より証拠で、実際にコードを実行して比べてみましょう。

まず、読み込み対象のテキストファイル test2.txtとして、

rt aaaaaaaaaaa
u ssssssss

dddddd

のように、空白文字(半角スペース)を入れてみましょう。rt と aaaaaaaaaaa のあいだに半角スペースが一文字あります。


そしてコードは、(Fedora30 の gccコンパイルで確認)

fgetsのほうは

#include <stdio.h>

int main()
{
	FILE *fp = fopen("test1.txt", "r"); // ここを読み取りモード"r"にするのを忘れないように
	if (fp == NULL) {
		perror("ファイルを開けませんでした。\n");
		return 1;
	}
	else {
		printf("ファイルをオープンしました。\n");
	}
	
	char buffer1[150];
	printf("文字列を読み取っています。\n");
	
	fgets(buffer1,150,fp);

	printf("1回目に読み取った文字列\n");
	printf("%s", buffer1);

	fgets(buffer1,150,fp);
	printf("2回目に読み取った文字列\n");
	printf("%s", buffer1);

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


このコードでは、printf の%sの後ろには改行文字(\n)をつけていません。なぜなら、fgets ではファイル読み込み時に改行文字も一緒に読み込むからです。


実行結果
ファイルをオープンしました。
文字列を読み取っています。
1行目に書いてある文字列
rt aaaaaaaaaaa
2行目に書いてある文字列
u ssssssss
ファイルをクローズしました。


です。





いっぽう、fscanf のほうは

#include <stdio.h>

int main()
{
	FILE *fp = fopen("test1.txt", "r"); // ここを読み取りモード"r"にするのを忘れないように
	if (fp == NULL) {
		perror("ファイルを開けませんでした。\n");
		return 1;
	}
	printf("ファイルをオープンしました。\n");

	char buffer1[150];
	printf("文字列を読み取っています。\n");
	
    if (fscanf(fp, "%s", buffer1) == EOF) {
        perror("");
        return 1;
    }

    printf("1回目に読み取った文字列\n");
	printf("%s", buffer1);

    if (fscanf(fp, "%s", buffer1) == EOF) {
        perror("");
        return 1;
    }
    printf("2回目に読み取った文字列\n");
	printf("%s", buffer1);

	fclose(fp);
	printf("ファイルをクローズしました。\n");
}
実行結果
ファイルをオープンしました。
文字列を読み取っています。
1行目に書いてある文字列
rt2行目に書いてある文字列
aaaaaaaaaaaファイルをクローズしました。

です。


このように、fcanf のほうでは、空白文字があるので、最初の1回目のfscanfでは、「rt」までしか読み込みません。

しかも改行を読み込まないので、 「rt」と「2行目に書いてある文字列」が続いて

rt2行目に書いてある文字列

と表示されてしまっています。

そして、2回目の fscanf で「aaaaaaaaaaa」を読み込んでいます。

CSVファイルの入出力[編集]

fscanfによる入門的方法[編集]

CSVファイルという、カンマで区切った形式のファイルがある。

たとえば、 次のようなデータです。

ファイル名「sample.csv」

名前,番号
山田,1
佐藤,2


なお、表計算ソフト( libreOffice の Calc など)で、表計算ファイルをCSVファイルに変換することができる。 表計算ソフトのそれぞれのセルにデータを入力したあと、表計算ファイルを「名前をつけて保存」するときに、ファイル形式を選択する欄があるので、その欄でCSV形式を選べばいい。


さて、CSVファイルをカンマで区切って一つずつ読み取りたい場合、

fscanf関数では %[^ ]という関数を使う。%[^ ]は、そのカッコの中にある文字をみつけるまで文字を読み込む。また、そのカッコ内の文字は保持しない。

たとえば %[^,]なら、カンマ記号(,)の手前まで文字を読み込み、カンマ記号じたいは除いて読み込む。

なので、%[^,]なら、結果的にカンマ文字を区切り文字として扱うことになる。


CSV読み取りファイルのプログラム例は、たとえば次のようなファイルになる。

(Fedora30 で確認)

#include <stdio.h>

int main()
{
	FILE *fp = fopen("sample.csv", "r");
	if (fp  == NULL) { // ここを読み取りモード"r"にするのを忘れないように
		perror("ファイルを開けませんでした。\n");
		return 1;
	}
	printf("ファイルをオープンしました。\n");

	char str1[150];
	char str2[150];

	printf("文字列を読み取っています。\n");
	
    if (fscanf(fp, "%[^,],%s,%s", str1,str2) == EOF) {
        perror("");
        return 1;
    }

	printf("str1として読み取った文字列\n");
	printf("%s\n", str1);

	printf("str2として読み取った文字列\n");
	printf("%s\n", str2);

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


実行結果 (Fedora30 で確認)

ファイルをオープンしました。
文字列を読み取っています。
str1として読み取った文字列
名前
str2として読み取った文字列
番号
ファイルをクローズしました。


fgetsによる方法[編集]

簡単な例[編集]

さきほどの章では、説明の都合上、fscanfを使ったが、じつは、fscanfでCSVファイルを作るのは、あまり実用的ではない。

なぜなら、まず空白文字(半角スペース)で読み込みを停止してしまうので、半角スペースを含む文字列の読み込みが出来無いからです。


なので、CSVの読み取りファイルをC言語で作る場合、fgets関数を作るほうが良い。


しかし、fgets には、%[^,] のようなカンマで読み込み中止関数をする都合いい機能が無い。


なので、いったんfgetsで、その行の全体を読み込んだ後に、 strtok という別の関数を使うのが良い。


strtok 関数とは、指定した文字列の直前までを読み込み機能のある関数なので、この関数でカンマ文字まで読み込むことを関数すればいい。 strtok では、指定した文字列じたいは読み込まない。

なお、 strtok 関数を使うためには #include <string.h> が必要です。

書式は

strtok(分解する文字列のある変数,"区切り文字にしたい文字")

のようになる。

CSVファイルを読み込ませたいなら、区切り文字にしたいのはカンマ記号なので、

strtok(分解する文字列のある変数,",")

となる。

なお、 strtok の第一引数を「NULL」にすると、前の文字の続きから読み込む。つまり

strtok(NULL,"区切り文字にしたい文字")

にすると、前の文字の続きから次ぎの区切り文字まで(その行に残りの区切り文字がない場合には行末まで)を読み込む。


コードは下記のようになる。

(Fedora30 で確認)

#include <stdio.h>
#include <string.h>

int main()
{
	FILE *fp = fopen("sample.csv", "r"); // ここを読み取りモード"r"にするのを忘れないように
	if (fp == NULL) {
		perror("ファイルを開けませんでした。\n");
		return 1;
	}
	printf("ファイルをオープンしました。\n");
	
	char buffer1[150];
	printf("文字列を読み取っています。\n");
	
	fgets(buffer1,150,fp);

    printf("1行目の文字列\n");
	printf("%s", buffer1);

	char str1[150];
	char str2[150];

	strncpy(str1, strtok(buffer1,",") ,150); // str1 = strtok(buffer1,","); では代入できない。
	strncpy(str2, strtok(NULL,",") ,150);

    printf("str1として読み取った文字列\n");
	printf("%s", str1);
	printf("\n");

    printf("str2として読み取った文字列\n");
	printf("%s", str2);

	fgets(buffer1,150,fp);
    printf("2行目の文字列\n");
	printf("%s", buffer1);


	strncpy(str1, strtok(buffer1,",") ,150);
	strncpy(str2, strtok(NULL,",") ,150);

    printf("str1として読み取った文字列\n");
	printf("%s", str1);
	printf("\n");

    printf("str2として読み取った文字列\n");
	printf("%s", str2);

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


実行結果
ファイルをオープンしました。
文字列を読み取っています。
1行目の文字列
名前,番号
str1として読み取った文字列
名前
str2として読み取った文字列
番号
2行目の文字列
山田,1
str1として読み取った文字列
山田
str2として読み取った文字列
1
ファイルをクローズしました。


上記のように、たしかに、読み取った文字列を分解している。


要素数の多い場合の例[編集]

さきほどの例では、説明の単純化のため、たったの各行あたり、要素数はたった2個だった。

しかし、実際の場合は、もっと要素数が多い。

たとえば、

名前,番号,国語の得点,数学の得点
山田,1,80,90
佐藤,2,70,100

のように、各行あたり4個のデータを読み込む場合を考えよう。


コード例
#include <stdio.h>
#include <string.h>

#pragma warning(disable:4996)

int main()
{
	FILE* fp = fopen("sample.csv", "r");
	if (fp == NULL) { // ここを読み取りモード"r"にするのを忘れないように
		perror("ファイルを開けませんでした。\n");
		return 1;
	}
	else {
		printf("ファイルをオープンしました。\n");
		printf("\n");
	}


	char buffer1[150];
	printf("文字列を読み取っています。\n");

	struct seisekihyou {
		char str[150];
	};

	struct seisekihyou row[20]; // 構造体配列の宣言

        // 下記のline は読み取りたい行数
	for (int line = 1; line <= 2; line = line + 1) {
		fgets(buffer1, 150, fp);

		printf("%d行目の文字列\n",line);
		printf("%s", buffer1);


		strncpy(row[0].str, strtok(buffer1, ","), 150); // str1 = strtok(buffer1,","); では代入できない。

		printf("row[%d].strとして読み取った文字列\n", 0);
		printf("%s\n", row[0].str);

                // 下記のtempは1行あたりの読み取りたい項目数
		for (int temp = 1; temp <= 3; ++temp) {
			strncpy(row[temp].str, strtok(NULL, ","), 150);

			printf("row[%d].strとして読み取った文字列\n", temp);
			printf("%s\n", row[temp].str);
		}
	
		printf("\n");
	}


	fclose(fp);

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

	return 0;
}


実行例
ファイルをオープンしました。

文字列を読み取っています。
1行目の文字列
名前,番号,国語の得点,数学の得点
row[0].strとして読み取った文字列
名前
row[1].strとして読み取った文字列
番号
row[2].strとして読み取った文字列
国語の得点
row[3].strとして読み取った文字列
数学の得点


2行目の文字列
山田,1,80,90
row[0].strとして読み取った文字列
山田
row[1].strとして読み取った文字列
1
row[2].strとして読み取った文字列
80
row[3].strとして読み取った文字列
90


ファイルをクローズしました。

このウィンドウを閉じるには、任意のキーを押してください...
解説

前の節では、単純化のために配列や構造体やfor文は用いなかったが、実務では用いたほうがイイだろう。


各行の最初の項目だけ strncpy(row[0].str, strtok(buffer1, ","), 150); のようにstrtokの第一引数を指定する必要がある。


いっぽう、strncpy(row[temp].str, strtok(NULL, ","), 150); のようにstrtokの第一引数がnullなら、strtokは以前の区切り文字から次の区切り文字までを抜き取るだけなので、2番目の項目からはfor文で使いまわしができる。

if文との組み合わせ[編集]

fgets関数やstrtok関数などで読み取った文字列をもとに、if文などの条件分岐関数と組み合わせる際、標準C言語のif文では、文字列にはイコール記号== など算術的な記号での比較ができません。

これはつまり、Cでは、文字列の比較にイコール記号が使えないという事です。

標準C言語では、文字列どうしの比較をする際、 strcmp という関数を使います。

strcmpについて詳しくは『C言語/制御文』をお読みください。


Linux の場合[編集]

一例として、Fedora28でファイル入出力を実行する場合のコードと実行結果の例を下記にしめします。

テキストファイルの文字コードはUTF-8です。Fedoraの標準の文字コードのままです。テキストエディタにはGedit(ジーエディット)を用いている。GeditはFedora28で標準に付属してくるテキストエディタです。

#include <stdio.h>

int main()
{
	FILE *fp = fopen("test1.txt", "r"); // ここを読み取りモード"r"にするのを忘れないように
	if (fp == NULL) {
		perror("ファイルを開けませんでした。\n");
		return 1;
	}
	printf("ファイルをオープンしました。\n");

	char str1[150];
	printf("文字列を読み取っています。\n");

	fscanf(fp, "%s", str1);
	printf("ファイル1行目に書いてある文字列\n");
	printf("%s\n", str1);

	fscanf(fp, "%s", str1);
	printf("ファイル2行目に書いてある文字列\n");
	printf("%s\n", str1);

	fclose(fp);
	printf("ファイルをクローズしました。\n");
	return 0;
}
実行結果
ファイルをオープンしました。
文字列を読み取っています。
ファイル1行目に書いてある文字列
aaaaaaaaaaa
ファイル2行目に書いてある文字列
ssssssss
ファイルをクローズしました。

Fedora28の場合、読み取り対象のテキストファイルの文字コードを何に変えても、コマンドプロンプトで正しく表示される。

なお、Geditの変換可能な文字コードの一覧に「日本語 (CP932)」 というのがあるが、これがマイクロソフト Shift-JIS のことである(つまり マイクロソフト自称「ANSI」)。もし、マイクロソフト自称ANSIとLINUX用テキストファイルを共通化したい場合、 CP932 を選べばいい。

とはいえ、最近のWindows用テキストエディタでは(Linuxでは標準的に採用されている)UTF-8も表示できるのが一般的なので、特に文字化けなどの起きないかぎり、むりに文字コードを変える必要は無い。


バイナリーファイルの読み書き[編集]

バイナリーファイルとしての書き込み[編集]

C言語のプログラムで、バイナリーファイルの読み書きをしたい場合、

fopen の引数で「r」(読み込み)とか「w」(書き込み)とか編集モードの指定がありますが、それに「b」をつけます。つまり「rb」や「wb」などの引数になります。

また、書き込む関数には fwrite を使う必要があります。 いっぽう、 fprintf では、テキストファイルに自動的に変換してしまいます。

たとえば下記のようになります。

//#include "stdafx.h"
#include <stdio.h>

#pragma warning(disable:4996)

int main()
{
	FILE *fp = fopen("test1.bin", "wb");
		
	if (fp == NULL) {
		perror("ファイルを開けませんでした。\n");
		return 1;
	}
	else {
		printf("ファイルをオープンしました。\n");
	}

	printf("バイナリファイルに書き込んでいます。 \n");
	
    char buf[5] = {0x42,0x4d,3,4,5};
    fwrite(buf, 1, 5, fp);
         
	fclose(fp);
	printf("ファイルをクローズしました。\n");
}

書き込みできたか否かを確認するには、バイナリエディタ(あるいは「16進エディタ」などと言われる)で確認してください。

0x42 などの冒頭の 0x は16進数であることを表す。バイナリーファイルの読み書きに限らず、一般にC言語で16進数をあつかう場合は、16進数である数に接頭辞 0x をつけて区別する。数値 0~9 までは0x をつけなくても十進数と同じなので省略できる。

バイナリエディタで読み込み

42 4D 03 04 05

と書き込まれていることが確認できれば成功です。


読み取り[編集]

バイナリファイルの読み取りには fread を使うことがある。

いきなりバイナリファイルを読み取るのは初心者には難しいので、まずテキストファイルを読み取る実験をしてみよう。

以前に作った test1.txt ファイルを読み取るとしよう。

test1.txt

aaaaaaaaaaa
ssssssss

dddddd

コード例

#include <stdio.h>

#pragma warning(disable:4996)

int main()
{
	FILE *fp = fopen("test.txt", "r");
		
	if (fp == NULL) {
		perror("ファイルを開けませんでした。\n");
		return 1;
	}
	else {
		printf("ファイルをオープンしました。\n");
	}


    char str1[150];
	printf("文字列を読み取っています。\n");
	    
    fread(str1, sizeof(unsigned char), sizeof(str1) / sizeof(str1[0]), fp);
        
    printf("ファイルに書いてある文字列\n");
	printf("%s\n", str1);

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


実行結果
ファイルをオープンしました。
文字列を読み取っています。
ファイルに書いてある文字列
aaaaaaaaaaa
ssssssss

dddddd
ファイルをクローズしました。


では、バイナリーデーターを読み取ろう。

コード例

#include <stdio.h>

#pragma warning(disable:4996)

int main()
{
	FILE *fp = fopen("test1.txt", "rb");
		
	if (fp == NULL) {
		perror("ファイルを開けませんでした。\n");
		return 1;
	}
	else {
		printf("ファイルをオープンしました。\n");
	}

        char str1[70]; // 表示結果を短くするために数値を微妙に小さくした
	printf("バイナリーデーターを読み取っています。\n"); // 「文字列」ではなくバイナリーデーター
	
    fread(str1, sizeof(char), 50, fp);
            
    printf("ファイルに書いてあるバイナリーデーター\n");
	
	for (int i=0 ; i < sizeof(str1) / sizeof(str1[0]); i = i+1)
	{
		printf("%02x ", str1[i]); // 最低でも2桁を表示、の意味
	}
	
	fclose(fp);
	printf("\nファイルをクローズしました。\n");

}


実行結果
ファイルをオープンしました。
バイナリーデーターを読み取っています。
ファイルに書いてあるバイナリーデーター
61 61 61 61 61 61 61 61 61 61 61 0d 0a 73 73 73 73 73 73 73 73 0d 0a 0d 0a 64 64 64 64 64 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
ファイルをクローズしました。
※ ファイル作成時・保存時の文字コードの種類によっては改行文字 0d 0a などの制御文字の内容が若干違うかもしれません。

脚註[編集]