1週目(1):ファイル操作とコマンドライン引数

サンプルプログラム1は,テキストファイルを入力とし,各行に行番号をつけ,別のファイルに出力する処理を行うプログラムである.

作業課題

  1. ex4_1という名前で新規プロジェクト(Win32 Console Application)を作成後,ex4_1.cという名前のcソースファイルを作成し,サンプルプログラム1を入力しなさい. 入力後,ビルドを行い,コンパイルエラーがないことを確認しなさい.
  2. (各自のディレクトリ)\ex4_1\Debugの下に新規テキストドキュメントを作成しなさい.ファイル名はsample.txtとすること.
  3. sample.txtを開き,5行程度の文章を入力し,保存しなさい.
  4. コマンドプロンプトを起動し,”cd   (各自のディレクトリ)\ex4_1\Debug”と入力して,実行ファイルのあるディレクトリに移動しなさい.
  5. コマンドプロンプト上で”dir”と入力し,ディレクトリ内に2つのファイル(ex4_1.exesample.txt)があることを確認しなさい.
  6. コマンドプロンプト上で”ex4_1 sample.txt result.txt”と入力して,プログラムを実行しなさい.
  7. プログラムの実行後に作成されるresult.txtを開き,sample.txtの各行に行番号がついていることを確認しなさい.

※コマンドプロンプトの基本操作については演習I第1回資料を読んで思い出しておくこと.

> Z:
> cd (各自のディレクトリ)\ex4_1\Debug
> ex4_1 sample.txt result.txt

解説

(1)コマンドライン引数 (17行目)

これまでの演習では,メイン関数に引数がない例を多く扱ってきた.本課題では,ソースの17行目に示すように,メイン関数に引数を与えている.これにより,コマンドライン引数を処理できるようになる.

int main(int argc, char* argv[])

main 関数の最初の引数 argc は argv に格納されている引数の数を表す整数.プログラム名も引数として渡されるので,必ず 1 以上の値になる.

2番目の引数 argv は ”「文字列を指すポインタ」 の配列 ”を指すポインタ で,”「文字列を指すポインタ」 の配列 ”の末尾にはNULLが入る.(下図でargv[3]にNULLが入っている点に注目.)プログラムのユーザーが入力したコマンドライン引数を表す.argv[0] にはプログラムを起動したコマンド名へのポインタ,argv[1] には最初のコマンドライン引数へのポインタが格納される.

コマンドプロンプト上で,

> ex4_1.exe sample.txt result.txt

と入力した場合,argc=3,argv[0]="ex4_1.exe",argv[1]="sample.txt",argv[2]="result.txt"が格納され,メインルーチンに渡される.このときの,概念図を以下に示す.

(2)ファイルのオープン (28行目,33行目)

ファイルの読み書きの前には,ライブラリ関数fopenでファイルをオープンする必要がある.fopen関数の プロトタイプ宣言は次の通りである.

FILE *fopen(const char *name, const char *mode);

fopenを使用するには,入出力ライブラリ<stdio.h>をインクルードする必要がある.fopenは,ファイル名およびモードを引数とし,ファイル読み書きに必要なポインタ(ファイルポインタ)を返す関数である. ファイルのオープンに失敗した場合はNULLを返す.

使えるモードは,読み込み(""),書き出し("w"),追加("a")の3種である.また,画像データなどのバイナリファイルをオープンする際には,モードの文字列の末尾にbを付加する.ファイルポインタは,ファイルバッファの位置,バッファ内の現在の文字位置,ファイルが読み書きのどちらをされるのかなどファイルに関する情報を含む構造体を指している.また,この構造体はFILEという型名が付けられており,typedefを用いてstdio.h内で定義されている.

fopenの使用例1:テキストファイルsample.txtを読み込みモードでオープン.

FILE *fp;
char name[20] = "sample.txt";
fp = fopen(name, "r");

fopenの使用例2:バイナリファイルfoo.rawをバイナリ書き込みモードでオープン.

FILE *fp;
fp = fopen("foo.raw", "wb");

(3)ファイルのクローズ (42行目,43行目)

関数fcloseはファイルを閉じる関数である.関数のプロトタイプ宣言は以下の通り.オープンしたファイルは,最後に必ず閉じること.

int fclose(FILE *fp);

fcloseの使用例:fpが指しているファイルを閉じる.

fclose(fp);

(4)ファイル入出力に使用される関数 (11,12,23行目)

■入力に使用される関数

int getc(FILE *fp);

fp から1文字読み込む.ファイル終了またはエラーのときはEOFを返す.

char *fgets(char *line, int maxline, FILE *fp);

fp から,改行(\n)あるいは(maxlineで指定した文字数-1)までの1行を,文字配列lineに読み込む.戻り値は,lineへのポインタ.ファイル終了時にはNULLが返る.(サンプルソース10行目で使用)

int fscanf(FILE *fp, const char *format, ...);

fp からの書式付き入力.戻り値は,正しく変換し割り当てたフィールドの数.戻り値が 0 の場合は、フィールドの代入が行われていないことを意味する.エラーが発生するか,最初の変換の前にファイルストリームの終端を検出するとEOFを返す.

size_t fread(void *buffer, size_t size, size_t count, FILE *fp);

書式なしデータをストリームから読み出す関数.fp から size バイトの項目を count 数まで読み出し,buffer に格納する.戻り値は,実際に読み出した全項目の数.

■出力に使用される関数

int putc(int c, FILE *fp);

fpへ文字を出力.エラー時にはEOFを返す.

int fputs(const char *line, FILE *fp);

ファイルに文字列lineを書き込む.エラーが起きるとEOFを返し,そうでなければ0を返す.

int fprintf(FILE *fp, const char *format, ...);

ファイルへの書式付き出力.戻り値は出力したバイト数.(サンプルソース11,22行目で使用)

size_t fwrite(const void *buffer, size_t size, size_t count, FILE *fp);

書式なしデータをストリームへ書き込む関数.size バイトの項目を count 数だけ buffer から,fp へ書き込む.戻り値は,実際に書き込んだ項目数.

(5)ファイルの終端 EOF (End Of File)

入出力ルーチンがファイルの終端に達したとき (またはエラーの場合) にはEOFが返される.EOFはファイルの終わりを表す記号定数で,stdio.hの中で定義されている.

サンプルプログラム1では,ファイルの読み込みにfgets関数を用いたため,fgets関数の戻り値がNULLかどうかでファイルの終端を判断したが,関数によっては,EOFでファイル終端を検出する場合があることを覚えておきたい.

(6)標準入出力 (23行目)

定義済みのファイルポインタには,

の3つがある.標準入力,標準出力,標準エラー出力は,プログラムの開始時にOSによって自動的に用意されるファイルである.通常stdinはキーボード,stdout,stderrは画面につながれている.

標準入力,標準出力,標準エラー出力については, 演習I 資料にも説明があるので,思い出しておくと良い.

(7)perror (29行目,34行目)

システムエラーメッセージを表示する関数.表示する文字列メッセージを引数とする関数であり,戻り値はない. プロトタイプ宣言は以下のようになっている.

void perror(const char* string);

最初に string が表示され、次にコロン、さらにエラーを起こした最後のライブラリ呼び出しのシステムエラーメッセージと続き、最後に改行文字が出力される.string を NULL ポインタまたは NULL 文字列へのポインタにすると、perror 関数はシステム エラー メッセージだけを表示する.

使用例(ソースリスト28行目):

perror("Input file error");

実行例: samplee.txtという入力ファイルは存在しないものとし,以下のようにコマンドライン引数を指定して,実行してみる.

> ex4_1.exe samplee.txt result.txt

実行結果として,以下のような文字列が,コマンドプロンプト上に表示される.

Input file error: No such file or directory

後半の”No such file or directory”の部分がライブラリ呼び出しのシステムエラーメッセージである.

ファイルオープンの後で呼び出すperror 関数には, エラーの原因を特定しやすくするため,引数としてファイル名を与えてもよい.

(付録) ファイルの扱いに関する補足事項

フォルダ(ディレクトリ)の階層構成とパスの関係

ファイル操作に関するその他の関数