第 9 章 ファイルとの入出力 45
9.3 ファイルへの入力/ファイルへの出力を行う関数
ファイルとの入出力関係を担う関数は非常に多くのものが既にライブラリ関数として用 意されている.ここでは,C言語の基礎的プログラミングを行うのに必要なものに限定し て,標準的かつ基礎的な入出力関数について学ぶことにする.
fgetc : ファイルからの一文字入力
関数fgetc は, 指定されたファイルから,1文字だけ読み込む関数である. ファイルポ
インタfpで指定されたファイル(のポインタによって指し示された位置)の文字を1つだ け取り込む.ただし,ファイルの終端コード(EOF = End Of File)を返すことを可能と するために,戻り値はchar型でなく,int型として定義されている点に注意すること.も ちろん,戻り値を受けるための変数も,原則的にint型する必要がある2.
FILE *fp;
int moji;
if((fp = fopen("input.dat.c","r")) == NULL){
printf("File can not open! \n");
exit(1);
}
moji = fgetc(fp);
....
fputc : ファイルへの一文字出力
関数fputc は, 指定されたファイルに, 指定された1文字を出力する関数である. ファ
イルポインタfpで指定されたファイルに変数c(int型)に格納された文字を出力する場合 の記述は以下のようになる.
FILE *fp;
int c;
...
fputc(c, fp);
fgets : ファイルからの一行入力
関数fgets は, 指定されたファイルから,一行の文字列,すわなち行の先頭から終端の
改行記号3までを読み込む.ただし,永遠に改行記号が見つからないと暴走する恐れが生 じるため,以下のように第2引数に読み込む文字数の上限を与えて,それを制限する.第 1引数は文字配列,第3引数はファイルポインタをそれぞれ指定する.
2 もちろん,戻り値をchar型に型変換(キャスト)してからchar型の変数に格納しても良い.ただし,
キャストする前にEOFか否かの判定が必要となる
3MS-Windows系のPCでは,改行記号は<CR><LR> (キャリッジリターンとラインフィード)である.
UNIX(Linux)での改行記号は<LF>, Machintoshでは<CR>のみである.
第9章 ファイルとの入出力 48 FILE *fp;
char s[256];
...
fgets(s, 256, fp);
なお,fgets関数は,戻り値として引数の文字バッファの先頭アドレスを返します(一般 には,あまりそれを利用しません).ファイルの終端に達して文字列が読み込めない場合 や,何らかのエラーによって文字列の読み込みが不可能だった場合にはNULLポインタ を返します.
fputs : ファイルへの一行文字列の出力
関数fputsは,上記の関数fgetsの逆の処理を行う関数であり,文字配列(バッファ)
に格納された一行の文字列を,指定されたファイルに出力する.第1引数は文字配列,第 2引数はファイルポインタを指定する.なお,文字数 nには一行の終端を表すエスケープ 文字(¥n)が含まれるため,実際に格納することができる文字数は n-1 である.
fputs(s, 256, fp);
例題 9–1 ファイルから1文字ずつ文字を読み込み,画面に出力するプログラムを作成せよ. 解答例 9–1
1: #include <stdio.h>
2: #include <stdlib.h>
3: int main(voiod){
4: int c;
5: char fname[24];
6: FILE *fp;
7:
8: printf("ファイル名を入力してください\n");
9: scanf("%s", fname);
10:
11: if((fp = fopen(fname, "r")) == NULL){
12: printf("エラー!ファイルが読めません.終了します.\n");
13: exit(1);
14: }
15: while((c = getc(fp)) != EOF) { 16: putchar(c);
17: }
18: fclose(fp);
19: return(0);
20: }
実行する前に,何らかのテキストファイルを作成しておくこと(入力する文字列は任意). このプログラムでは,getc関数がファイルの最後でEOF(-1)を返すことを利用して, while 文の修了判定を行っていることに注意されたい.
第9章 ファイルとの入出力 49
例題 9–2 ファイルから1行ずつ文字列を読み込み,そのまま一行ずつ別のファイルに出力 するプログラムを作成せよ.
解答例 9–2
1: #include <stdio.h>
2: #include <stdlib.h>
3:
4: int main(void){
5: char fname1[24], fname2[24];
6: char buf[256];
7: FILE *fp1, *fp2;
8:
9: printf("入力ファイル名は?\n");
10: scanf("%s", fname1);
11: printf("出力ファイル名は?\n");
12: scanf("%s", fname2);
13:
14: if((fp1 = fopen(fname1, "r")) == NULL){
15: printf("エラー!入力ファイルが開けません → 終了します.\n");
16: exit(1);
17: }
18: if((fp2 = fopen(fname2, "w")) == NULL){
19: printf("エラー!出力ファイルが開けません → 終了します.\n");
20: exit(1);
21: } 22:
23: while( fgets(buf, 256, fp1) != NULL ) {
24: fputs(buf, fp2);
25: }
26: fclose(fp1);
27: fclose(fp2);
28: return(0);
29: }
※上記例題の24行目を,fprintf(fp2, "%s", buf); と書き換えても同じ結果が得ら れることを確認しておくこと!
第9章 ファイルとの入出力 50
fprintf : 書式付きファイル出力
fprintf関数は, 出力先がファイルであることを除き, printf関数とほぼ同様に用いる
ことができる. 出力先がファイルであるため, 引数の先頭がファイルポインタとなる. 文
字列 ”Hello World” をファイル(fp)に出力する場合は以下のようになる. なお,下記の例
では出力文字列の最後にて改行処理を行っている. fprintf(fp, "Hello World\n");
fscanf : 書式付きファイル入力
fscanf はファイルからの書式付き入力を行う関数であり, 用い方は先に述べた scanf
関数とほぼ同様である. 一番目の引数としてファイルポインタを指定すればよい. ファイ ルポインタがfpで, 指定された先のファイルから数値を読み込み, それを整数型の変数a に格納する場合の記述は以下のようになる.
fscanf(fp, "%d", &a);
例題 9–3 以下のように,名前と点数が列挙されたファイルを作成し,最高得点を獲得 した者の名前と,その得点を画面に表示させるプログラムを作成しなさい.
Arai 70
Matsumoto 85
Morita 80
...
解答例 9–3
1: #include <stdio.h>
2: #include <stdlib.h>
3: int main(void){
4: char name[256]; /* 名前を格納する配列 */
5: char top_name[256]; /* 最高得点者の名前を格納する配列 */
6: int point; /* 得点を格納する変数 */
7: char fname[24]; /* 入力ファイル名*/
8: FILE *fp; /* ファイルポインタ */
9: int max_point = 0; /* 最高得点の保存用変数 */
10:
11: printf("入力ファイル名は?\n");
12: scanf("%s", fname);
13:
14: if((fp = fopen(fname, "r")) == NULL){
15: printf("エラー!入力ファイルが開けません → 終了します.\n");
16: exit(1);
17: }
18:
第9章 ファイルとの入出力 51
19 /* while文でループを構成...*/
20: while( fscanf(fp, "%s %d", name, &point ) != EOF ){
21:
22: /* 得点の比較 → 現時点での最高得点ならば */
23: if ( point > max_point ){
24:
25: /* その点数を, max_pointに格納 */
26: max_point = point;
27:
28: /* 名前を top_name にコピー */
29: strcpy( top_name, name );
30: }
31: }
32: fclose(fp);
33: printf("最高得点:%s氏で,%d点でした\n", top_name, max_point);
34: return(0);
35: }
<注意>
• fscanf関数は,読み込みが成功しなかった場合,およびファイルの終端ではEOFを
返す.その性質を利用してwhileループを構成している点に注意せよ.
• 各行の名前は,一端配列nameに読み込まれる.その時点で最高得点ならば,top name を更新する.文字列間のコピーには,標準関数strcpyを用いた.
• このプログラム例では,最高得点者のインデックス(行数)は記憶する必要がない.
その代わりに,メモリに残るのは最高得点者のデータのみであり,その他のデーター はすべて捨てられる.
• 各行のデータを捨てずに保存しておき,最後に結果を表示させる形のプログラム例 を次のページの別解に示したので,参考にして欲しい.
第9章 ファイルとの入出力 52 解答例 9–3(別解)
1: #include <stdio.h>
2: #include <stdlib.h>
3: int main(void){
4: char name[100][256];/* 100名分の名前を格納する配列 */
5: int point[100]; /* 100名分の得点を格納する配列 */
6: char fname[24]; /* 入力ファイル名*/
7: FILE *fp; /* ファイルポインタ */
8: int index = 0; /* 最高得点のインデックス */
9: int max_point = 0; /* 最高得点の保存用変数 */
10: int i; /* ループ変数 */
11:
12: printf("入力ファイル名は?\n");
13: scanf("%s", fname);
14
15: if((fp = fopen(fname, "r")) == NULL){
16: printf("エラー!入力ファイルが開けません → 終了します.\n");
17: exit(1);
18: }
19:
20: /* 分りやすいので,for文を使います! */
21: /* バッファは最高で100名分までであることに注意... */
22: for ( i = 0; i < 100 ; i++ ){
23:
24: /* ファイルの終端にきたら,ループを終了します*/
25: if ( fscanf(fp, "%s %d", name[i], &(point[i]) ) == EOF )
26: break;
27:
28: /* 最高得点とそのインデックスを格納 */
29: if ( point[i] > max_point ){
30: max_point = point[i];
31: index = i;
32: }
33: }
34: fclose(fp);
35: printf("最高得点:%s氏で,%d点でした\n", name[index], point[index]);
36: return(0);
37:}
第9章 ファイルとの入出力 53