C言語編 第41章 テキストファイルの読み書きA

このエントリーをはてなブックマークに追加

この章の概要

この章の概要です。

fputc関数と fgetc関数

前章では、fputs関数(⇒リファレンス)と fgets関数(⇒リファレンス)を使ったファイル処理を扱いました。 これらの関数は、テキストデータを1行単位で扱うものでした。

今回は、1文字ずつの処理を扱ってみます。 ここで使うのは、fputc関数(⇒リファレンス)と fgetc関数(⇒リファレンス)です。

#include <stdio.h>
#include <stdlib.h>

static const char FILE_NAME[] = "hello.txt";
static const char PRINT_STR[] = "Hello, World";

int main(void)
{
	FILE* fp;
	char buf[80];
	int c;
	int i;


	/* ファイルへ書き出す */
	fp = fopen( FILE_NAME, "w" );
	if( fp == NULL ){
		fputs( "ファイルオープンに失敗しました。\n", stderr );
		exit( EXIT_FAILURE );
	}
	for( i = 0; PRINT_STR[i] != '\0'; ++i ){
		fputc( PRINT_STR[i], fp );
	}
	if( fclose( fp ) == EOF ){
		fputs( "ファイルクローズに失敗しました。\n", stderr );
		exit( EXIT_FAILURE );
	}


	/* ファイルから読み込む */
	fp = fopen( FILE_NAME, "r" );
	if( fp == NULL ){
		fputs( "ファイルオープンに失敗しました。\n", stderr );
		exit( EXIT_FAILURE );
	}
	for( i = 0; ; ++i ){
		c = fgetc( fp );
		if( c == EOF ){
			if( feof(fp) ){
				buf[i] = '\0';
				break;
			}
			else if( ferror(fp) ){
				fputs( "読み込み中にエラーが発生しました。\n", stderr );
				exit( EXIT_FAILURE );
			}
		}
		buf[i] = (char)c;
	}
	if( fclose( fp ) == EOF ){
		fputs( "ファイルクローズに失敗しました。\n", stderr );
		exit( EXIT_FAILURE );
	}


	/* 読み込んだ文字列を、標準出力へ出力 */
	puts( buf );

	return 0;
}

実行結果(標準出力)

Hello, World

実行結果(hello.txt)

Hello, World

前章でも fgets関数を例にとって、ファイルの終わりの検出を行いましたが、今回は fgetc関数で同じことをしています。

fgets関数の場合は、ファイルの終わりに来たときやエラーが発生したときに NULL を返しましたが、 fgetc関数の場合は、EOF(⇒リファレンス)というマクロを返します。

fgetc関数が EOF を返してきたときは、feof関数(⇒リファレンス)でファイルの終わりに来たかどうかを調べられます。これは fgets関数と同じです。 また、エラーの発生によって EOF を返してきたかどうかは、ferror関数で(⇒リファレンス)調べられます。
このように、fgets関数の場合と同様、戻り値の値だけでは、ファイルの終わりなのか、エラーの発生なのかは区別できませんから、 別の関数の助けが必要になります。

なお、一度 0以外を返すようになった ferror関数は、そのままでは、その後何度呼び出しても 0以外を返し続けます。 エラーをリセットするために、clearerr関数(⇒リファレンス)というものがあり、これを呼び出すと、エラー情報はクリアされます。

putc関数と getc関数

putc関数(⇒リファレンス)は、ストリームへ1文字を書き込む関数です。 この説明だと fputc関数と同じように聞こえますが、実際同じです。

fputc関数と putc関数の違いは、後者はマクロとして定義されている可能性があるという点だけです。 マクロで定義されていれば、関数呼び出しに伴う実行コストが掛からない分だけ、多少高速ですが、実引数の指定に注意が必要になります。 無用なトラブルを避けるためにも、普通は fputc関数を使った方が良いです。

また、putchar関数(⇒リファレンス)というものもあり、これは標準出力専用の putc関数と考えられます。 これもマクロとして定義されている可能性があります。


読み込み系の関数の事情も同様です。 getc関数(⇒リファレンス)は、マクロとして定義されている可能性がある fgetc関数と考えられます。
また、getchar関数(⇒リファレンス)は、標準入力専用の getc関数です。

fprintf関数と fscanf関数

これまでに1行単位の読み書き、1文字単位の読み書きが登場しました。 残すは、フォーマット指定付きの文字列の扱いです。

これは簡単で、今までに使ってきた printf関数(⇒リファレンス)や、 (これはあまり使っていませんが)scanf関数(⇒リファレンス)に、ストリームの指定が加わっただけです。 ストリーム指定版はそれぞれ、fprintf関数(⇒リファレンス)、 fscanf関数(⇒リファレンス)です。

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	FILE* fp;
	char str[41];
	int num;
	double dec;


	fp = fopen( "test.txt", "w+" );
	if( fp == NULL ){
		fputs( "ファイルオープンに失敗しました。\n", stderr );
		exit( EXIT_FAILURE );
	}

	/* 書き込み */
	fprintf( fp, "test %d %f\n", 123, 45.67 );

	/* ファイルの先頭へ巻き戻す */
	rewind( fp );

	/* 読み込み */
	fscanf( fp, "%40s %d %lf", str, &num, &dec );

	/* 標準出力へ */
	printf( "%s %d %f\n", str, num, dec );

	if( fclose( fp ) == EOF ){
		fputs( "ファイルクローズに失敗しました。\n", stderr );
		exit( EXIT_FAILURE );
	}

	return 0;
}

実行結果(標準出力)

test 123 45.670000

実行結果(test.txt)

test 123 45.670000

fprintf関数、fscanf関数のどちらも、第1引数に FILEオブジェクトへのポインタの指定が加わっているだけであり、それ以外に違いはありません。 fprintf関数であれば stdout(⇒リファレンス)を、 fscanf関数であれば stdin(⇒リファレンス)を指定すれば、それぞれ printf関数や scanf関数と同じ結果になります。


練習問題

問題@ 自分の使っているコンパイラにおいて、fputc関数、putc関数、putchar関数が、それぞれどのように定義されているか調べて下さい。 同様に、fgetc関数、getc関数、getchar関数について調べて下さい。

問題A 1文字ずつ読み書きする標準関数を使って、既存のテキストファイルのコピーを作り出すプログラムを作成して下さい。

問題B テキストファイルのデータ中に含まれているタブ文字を、半角空白に置き換えた結果を、別のファイルへ出力するプログラムを作成して下さい。 ただし、タブの幅は自由に決めて下さい。


解答ページはこちら

参考リンク

更新履歴

'2017/5/13 サンプルプログラムから、使われていないマクロの定義を削除。

'2017/5/11 FILE周りの用語について、表現を見直した。

'2015/8/29 flose関数の戻り値もチェックするようにした。

'2015/7/23 「fputc関数と fgetc関数」のサンプルプログラムで、ファイルへの書き出しの際に、文字列の終端文字が含まれないように修正。

'2010/5/16 新規作成。



前の章へ

次の章へ

C言語編のトップページへ

Programming Place Plus のトップページへ