パソコン活用研究シリコンバレー(C、C++、の活用研究)

ファイル入出力関数 (ファイルのオープン/クローズ)

=======================================================================================
ファイル入出力関数として、まずfopen, fclose, putc, fputc, getc, fgetc 等を取り上げてみます。
それ以外の入出力関数、ファイル操作関数の説明も補足しておきます。
=======================================================================================

ここで説明するファイル入出力関数は高水準入出力関数と呼ばれるもので、これとは別に
低水準入出力関数と呼ばれる関数もあります。(参照:低水準入出力関数

1 ファイルのオープン/クローズ

ファイルのオープン/クローズには、fopen, fclose を使います。
fopen書式は以下の通りですが、mode(アクセスモード)はコンパイラにより異なることがあります。

fopen
【書式】 FILE *fopen(filename, mode);
      char *filename;
      char *mode
返り値 成功の場合は、FILE構造体へのポインタ、失敗(エラー)の場合はNULL 

FILE はstdio.hに定義されているFILE構造体
filename はオープンするファイル名(パスを含む)
mode は以下のものを指定します。
 <アクセスモード>
"r" リードモード 指定したファイルがない場合はエラー。
"w" ライトモード ファイルがない場合は作成される
"a" 追加書き出しモード ファイルがない場合は作成される。ファイルがある場合は、ファイルの最後
   から追加。
"r+" リード/ライトモード。指定したファイルが存在しない場合はエラー
"w+" リード/ライトモード。ファイルがない場合は作成される。
"a+" リード/追加書き出しモード。ファイルがない場合は作成される。ファイルがある場合は、ファイル
    の最後から追加。
 <変換モード>
"t" テキストモード
"b" バイナリモード

例えば、data.binというバイナリーファイルをバイナリーモードでオープンする時は、
FILE *fp;
fp = fopen("data.bin", "rb");
のように記述します。

テキストモードでオープンすると、改行復帰の制御コード(&H0D, &H0A)、終端コード(&H1A)を制御コード
として処理します。

BASICでは書式がさまざまですが、data.txt というファイルを読み込みモードで開く場合は
<F-BASIC> open "I",#1,"data.txt"
<N88BASIC> open "data.txt" for input as #1
のような記述になります。

複数のファイルをオープンする場合、BASICでは#1,#2などの(ファイル番号)で、オープンされたファイルを
識別しますが、C言語では(上記の例では)fp (FILE構造体へのポインタ変数)でどのファイルかを識別します。
BASICプログラマにはFILE構造体というのが、ピンとこないかもしれません。それで最初はとっきにくい感じを
持つかもしれませんが、FILE構造体には、ファイルポインタ(現在読み書きしているデータの位置)や、
ファイルバッファへのポインタといったメンバーが用意されており、ファイルの取り扱いに必要な情報が
保持されています。
FILE構造体については、FILE構造体を参照して下さい。

fcloseの書式は以下の通りです。

fclose
【書式】
int fclose(fp);
FILE *fp;

返り値は、0 クローズ成功   EOF 失敗 


2 バイト入出力
オープンしたファイルに対し、1バイトづつ入出力を行う関数です。

関数 書式 返り値 機能
getc int getc(fp);
FILE *fp;
読み込んだ文字 fpが示す位置(ファイルポインタ)から1文字読み込み、ファイルポインタを
1つ進める。
putc int putc(c,fp);
int c;
FILE *fp
書き出した文字 fpが示す位置(ファイルポインタ)にcを書き出し、ファイルポインタを1つ
進める。

まったく同じ機能の関数としてとして、fgetc,fputcがあります。これらはライブラリ関数なのか、マクロ定義なのか
(getc,putc)の違いですが機能は同じです。ファイルサイズ重視か、実行速度重視かによって使い分けます。
(おじさんのつくる程度のプログラムじゃ関係ないけどね)
簡単な関数なので、使い方は、3 プログラム例を見てください。


3 プログラム例
簡単な例として、ファイルのコピーをするプログラムを作成してみます。
MS-DOSのcopyコマンドと同じ機能(ただし、ワイルドカードは使えないシンプルなもの)です。
>cp file1 file2
でfile1を複製してfile2を作ります。

エラー処理を除くと、最低限必要なのは赤字のところのコードだけです。
いたってシンプルなので、説明不要ですね。

/* cp.c  copy file   For LSIC */

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

main(argc, argv)
int argc;
char **argv;
{ 
  FILE *fp, *fp2;

if(argc != 3) {
 printf("Invarid arguments count");
 exit(0);}

if(NULL == (fp=fopen(argv[1],"rb"))) {
printf("Cannot open File : %s\n",argv[1]);
exit(1);}

fp2=fopen(argv[2], "wb");

while(feof(fp)==0) {
int c;
c=getc(fp);
if(ferror(fp)){
printf(" Read Error \n");
break;}

putc(c,fp2);
if(ferror(fp2)){
printf(" Write Error \n");
break;}
}

fclose(fp);
fclose(fp2);
}


テキストファイルを読み込んで表示するにも、これらのファイル入出力関数を使用した
プログラム例があります。


4 その他の入出力関数・ファイル操作関数

関数 書式 返り値 機能
fgets char *fgets(b, n, fp);
 char *b;
 int n;
 FILE *fp
読み込んだ文字のポインタ
エラーまたはEOFの時はNULL
fpが示す位置から)文字列をbに読み込む
文字は、
 ・n文字まで
 ・改行まで
 ・ファイルの終わりまで
のいずれかの条件が満たされるまで読み込まれる
fputs int fputs(b, fp);
 char *b;
 FILE *fp
書き出した最後の文字。
エラーの場合はEOFを返す
fpで示す位置に文字列bを書き出す。文字列の終わりの
NULL文字は書き出さない。
clearerr void clearerr(fp);
 FILE *fp
なし fpのエラーフラグ、EOFフラグをリセットする
feop int feof(fp);
 FILE *fp
ファイルエンドではない場合は0
ファイルエンドの場合は0以外
fpで示すファイルがエンドオブファイルかどうか調べる。
ferror int ferror(fp);
 FILE *fp
0 エラーなし
0以外 エラー
fpで示すファイルで読み込み、または書き出しエラーが
発生したかどうかを調べる。
fflush int fflush(fp);
 FILE *fp
0 エラーなし
0以外 エラー
fpで示すバッファをファイルに書き出す。
setbuf void setbuf(fp, b);
 FILE *fp;
 char *b;
なし デフォルトで指定されているバッファに代えて、bで指定
されるバッファを使う。

feop, ferror, clearerrについて
ファイルの入出力中にエラーが発生したり、エンドオブファアイル(ファイルの終わりに達した)が発生すると、
FILE構造体のフラグがセットされます。(参照 FILE構造体
それらのフラグがセットされているかどうかを調べるのが、feop, ferrorです。

また、それらのフラグは一度セットされると自動的に解除されるわけではないので、エラー処理を行った
あと等にフラグをリセットするのにclearerrを使います。

(補足)LSIC86のstdio.h

FILE構造体の定義
typedef struct {
char mode; /* file mode R, W, R/W */
char *ptr; /* next character position */
int rcount; /* number of characters left */
int wcount; /* number of rooms left */
char *base; /* location of buffer */
unsigned bufsiz; /* size of bufer */
int fd; /* file descriptor */
char smallbuf[1]; /* used for buffer when unbufferd */
} FILE;


LSIC86のFILE構造体の定義を見ると、FILE構造体にmodeという変数があり、これをフラグとして使用しているようです。
このmodeに以下のような値がセットされるようです。
リードモードなら0001、EOF(ファイルが終わりに達したら)0004
エラーが発生したら0010など

#define _READ 0001
#define _WRITE 0002
#define _EOF 0004
#define _ERR 0010
#define _BINARY 0020
#define _R 0040
#define _W 0100
#define _BUFF 0200

clearerr, feof, ferror関数はマクロ定義されており、mode変数をチェックしています。

#define clearerr(fp) ((fp)->mode &= ~(_EOF | _ERR))
#define feof(fp) ((fp)->mode & _EOF)
#define ferror(fp) ((fp)->mode & _ERR)



fflush, setbufについて何のために使うのか簡単に説明します。
今回ここで説明している関数は高水準入出力関数と言われる関数で、ディスクからの読み込みや
ディスクへの書き込みは、ディスクに対して直接行うのではなくバッファを介して行われます。
入出力関数 <--> バッファ <--> ディスク
ということになります。
(BASICでの説明ですが、バッファについてはファイルバッファをご参照下さい)

通常、バッファ <--> ディスク はシステム側で自動的に行うので、プログラム側で意識することは
ありませんが(*1)、これらをプログラム側から意識的に行うための命令がfflush, setbuf になります。
fflushはバッファにたまったデータを強制的にディスクに書き出します。
setbufはFILE構造体で指定しているバッファを変更します。

(*1)
通常は、バッファが一杯になるとシステム側で自動的にディスクの書き出しを行う。

TopPage