内 容
- ファイルとストリーム
- テキストファイルの読み込み・書き込み
- コマンドライン引数
- バイナリファイルの読み込み・書き込み
ファイルとストリーム
ファイル
ファイルの読み書きの対象にはハードディスク、USBメモリ、SDメモリカード、DVD-RWなど、さまざまなメディアがあります。もし、装置ごとにプログラムを作成しなければならないとすると、例えば、DVD-RWならばDVDドライブの制御プログラムから用意しなければなりません。新しいメディアが登場すればそれに対応したプログラムを追加していかなければなりません。これにユーザが作成するプログラムで対応するには無理があります。
では、実際には、この問題にどのようにして対処しているのでしょうか。直接のファイルへの読み書きはオペレーティングシステム(OS)によって制御されています。OSはメディアやリーダー/ライターといった物理的な装置を隠蔽し、異なるメディアへのデータの流れを
ストリームというオブジェクトに抽象化することで、ユーザーにも容易にファイルの読み書きの操作を記述できるようにしています。
ファイルに対する読み出し/書き込みの手順
- ファイルストリームを示す変数を宣言する。FILEはファイル入出力に必要なメンバ変数で構成された構造体です。
- ファイルに対して情報を読み/書きするために、fopen関数を使ってファイルをオープンします。引数にはファイル名と、読み込み、書き出し、追記などを指定するモード(下表を参照)を指定します。
- 読み込みならばfscanffやfgetsなどの関数を、書き出しならばfprintfやfputsなどの関数を利用してファイルの読み込み/書き出しを行います。
- ファイルをクローズします。
テキストファイルの書き込み・読み込み
1文字ずつの書き出し(fputc)
file_write.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp; //ストリーム
char filename[64] = "test.txt"; //ファイル名
char message[10] = "Algorithm"; //書き込む文字列
int i;
fp=fopen(filename,"w");
if(fp == NULL){ //ファイルオープン
fprintf(stderr, "Can't open %s.\n", filename); //エラーメッセージ
return EXIT_FAILURE; //異常終了する
}
for(i=0; i<10; i++)
fputc(message[i], fp); // 書き出し
fclose(fp); //ファイルクローズ
return 0;
}
主なファイル入出力関数
fopen関数 |
形 式 | FILE* fopen(char* filename, const char* mode) |
機 能 | ファイルfilenameをmodeで示されるモードでオープンする。 |
戻り値 | 成功:ファイルストリーム 失敗:NULL |
fclose関数 |
形 式 | int fclose(FILE *fp) |
機 能 | ファイルストリームfpをクローズする。 |
戻り値 | 成功:0 失敗:EOF |
fgetc関数 |
形 式 | int fgetc(FILE *fp) |
機 能 | ファイルストリームfpから1文字を読み込む。 |
戻り値 | 成功:読み込んだ文字 失敗:EOF |
fputc関数 |
形 式 | int fputc(int c, FILE *fp) |
機 能 | ファイルストリームfpへ1文字cを書き込む。 |
戻り値 | 成功:書き込んだ文字 失敗:EOF |
fgets関数 |
形 式 | int fgets(char *s, int n, FILE *fp) |
機 能 | ファイルストリームfpから読み取った1行分の文字列を文字列sへ格納する。改行を含む。ただし、1行の最大文字数はn-1で、文字列sの最後にナル文字(\0)を付加する。 |
戻り値 | 成功:読み込んだ文字s 失敗:NULL |
fputs関数 |
形 式 | int fputs(const char *s, FILE *fp) |
機 能 | ファイルストリームfpへ文字列sを書き込む。 |
戻り値 | 成功:正の値 失敗:EOF |
fscanf関数 |
形 式 | int fscanf(FILE *fp, const char *format, ...) |
機 能 | ファイルストリームfpから書式付き文字列formatを読み込む。 |
戻り値 | 成功:読み込んだ項目数 失敗:EOF |
fprintf関数 |
形 式 | int fprintf(FILE *fp, const char *format, ...) |
機 能 | ファイルストリームfpへ書式format付きの文字列を書き出す。 |
戻り値 | 成功:書き出した文字数 失敗:負の値 |
主なテキストファイルのモード一覧
モード | 説明 |
r | 読出し(read)専用モード(ファイルがなければNULLを返す) |
w | 書き込み(write)専用モード(ファイルが存在する場合は上書きする) |
a | 追記(append)専用モード(ファイルの最後に追加、ファイルがなければ新規作成) |
r+, w+, a+ | 更新モード(読み書きの両方が可能) |
標準入出力ストリーム
stderr、stdout、stdinはそれぞれ、標準エラー出力、標準出力、標準入力用のストリームです。これらのストリームはプログラムが起動されると同時に自動的にオープンされ、プログラムが終了するときに自動的にクローズされます。関数printfの出力先が標準出力(ディスプレイ)なのはstdoutに固定されているためで、関数scanfの入力元が標準入力(キーボード)なのはstdinに固定されているためです。
- EXIT_FAILURE プログラム終了時に実行環境へ返す値で、「異常終了」を示す。その値は、stdlib.hで定義されている。「正常終了」を示す定数としてEXIT_SUCCESSがある。
- EOF : End Of File ファイルの最後を示す。
1文字ずつの読み込み
file_read.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp; //ストリーム
char fname[] = "test.txt"; //ファイル名
char ch;
fp = fopen(fname,"r"); //ファイルオープン
if(fp == NULL){
fprintf(stderr,"Can't open %s.\n",fname);
return EXIT_FAILURE;
}
while((ch=fgetc(fp)) != EOF){ //EOFになるまで読み込む
putchar(ch); //表示
}
printf("\n");
fclose(fp); //ファイルクローズ
return 0;
}
1行ずつの書き出し
file_write_line.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp; //ストリーム
char filename[16] = "test.txt"; //ファイル名
char message[128];
int result,i=0;
printf("message>> ");
fgets(message,10,stdin); //保存先、読み取り文字数、標準入力
//ファイルオープン
fp = fopen(filename, "w");
if(fp == NULL){ //ファイルポインタがNULL(空)ならば
fprintf(stderr, "Can't open %s.\n", filename); //エラーメッセージの出力
return EXIT_FAILURE; //呼び出し元に異常終了を知らせる
}
// 書き出し
if(fputs(message,fp)==EOF){ //1行書き出し
fprintf(stderr,"Error: unable to write to output file.\n"); //エラーメッセージの出力
fclose(fp);
return EXIT_FAILURE; //異常終了
}
fclose(fp);
return 0;
}
1行文字列の読み込み
file_read_line.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
char fname[] = "test.txt";
char buff[256];
fp = fopen(fname,"r"); //ファイルオープン
if(fp == NULL){
fprintf(stderr,"Can't open %s.\n",fname);
return EXIT_FAILURE;
}
while(fgets(buff, sizeof(buff), fp) != NULL){ //1行読み込み
fprintf(stdout, "%s", buff); //標準出力
}
printf("\n");
fclose(fp);
return 0;
}
書式付き文字列の書き出し
file_writef.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp; //ストリーム
char filename[128] = "test.txt";
int count;
fp = fopen(filename, "w"); //ファイルオープン
if(fp == NULL){
fprintf(stderr, "Can't open %s.\n", filename);
return EXIT_FAILURE; //呼び出し元に異常終了を知らせる
}
// 書き出し
count=fprintf(fp,"%d,%s,%f\n",1,"八王子",36.7); //成功:文字数 失敗:−1
if(count==-1){
fprintf(stderr,"Can't open %s.\n",filename);
fclose(fp);
return EXIT_FAILURE; //異常終了
}
fclose(fp); //ファイルクローズ
return 0;
}
書式付き文字列の読み込み
file_readf.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
char filename[128] = "test.txt";
int no;
char loc[64]="";
double temp;
fp = fopen(filename, "r");
if(fp == NULL){
fprintf(stderr, "Can't open %s.\n", filename);
return EXIT_FAILURE;
}
fscanf(fp,"%d,%[^,],%lf",&no,loc,&temp); //^,カンマ以外の文字
printf("%f\n",temp);
fprintf(stdout,"%2d: %s %.1f\n",no,loc,temp);
fclose(fp);
return 0;
}
fscanfの書式 "%d,%[^,],%lf" について
読み取り対象のファイルtest.txtにはカンマ(,)で区切られた、整数、文字列、小数点を含む数字がテキスト形式で保存されています。このようなカンマ(,)もしくはタブで区切られたデータからなるファイルをCSV(Comma Separated Values)形式のファイルと呼びます。Excelなど表計算ソフトで取り扱われるファイル形式です。このCSV形式のファイルから文字列をfscanfを使って読み込む場合は、カンマ(,)も文字列として認識されてしまうため書式指定に%sをそのまま利用することはできません。カンマ(,)を含めず読み込むために、カンマ以外の文字を意味する書式指定%[^,]を指定します。
文字列・数値変換
文字列を数値へ変換
atof関数 | 文字列を浮動小数点数型に変換 | double atof(char *str) |
atoi関数 | 文字列を整数型に変換 | int atoi(char *str) |
atol関数 | 文字列を整数型に変換 | long atol(char *str) |
数値を文字列へ変換
itoa関数 | 整数int型nをd進数の文字列に変換 | char* itoa(int n, char *s, int d) |
ltoa関数 | 整数long型nをd進数の文字列に変換 | char* ltoa(long n, char *s, int d) |
演習
次のプログラムは、ファイルを読み込んで別の新しいファイルに書き出すコピープログラムです。空欄を埋めて完成させなさい。
fcopy.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *org, *cpy; //コピー元、コピー先
char org_fname[] = "copy.c";
char cpy_fname[] = "copy2.c";
char ch;
org = fopen(org_fname,"r");
if(org == [[空欄ア]]){
fprintf(stderr,"Can't open %s.\n",org_fname);
return EXIT_FAILURE;
}
cpy = [[空欄イ]];
if(cpy == NULL){
fprintf(stderr,"Can't open %s.\n",cpy_fname);
return EXIT_FAILURE;
}
while((ch=fgetc([[空欄ウ]])) != EOF){
[[空欄エ]](ch,cpy);
}
printf("\n");
[[空欄オ]];
[[空欄カ]];
return 0;
}
コマンドライン引数
ここまでのプログラム例の中に登場してきたmain関数の引数はすべてvoid型でした。一方、main関数には以下のような2つの引数を書くこともできます。この引数を利用することで、実行時にコマンドラインからのmain関数へ値を引き渡すことができるようになります。
int main(int argc, char *argv[])
argcには引き渡される値の個数が、argvにはargc個分の文字列が格納されています。
コマンドライン引数とmain関数に引き渡された値
次のサンプルプログラムを実行して、コマンドラインで入力した引数とmain関数に引き渡された値との関係を確認してみましょう。コマンドライン上での実行ファイル名自体が0個目の引数として渡されていることに注意しましょう。
args.c
#include <stdio.h>
int main(int argc, char *argv[]) //引数の個数、引数の文字列
{
int i;
printf("引数の数 %d", argc);
for(i=0; i<argc; i++)
printf("%d: %s", i, argv[i]);
return 0;
}
実行例
>args apple orange grape
引数の数 4
0: args
1: apple
2: orange
3: grape
コマンドラインからファイル名を指定しファイルを読み出す例
args_file_read.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) //引数の個数、引数の文字列
{
FILE *fp;
char buff[256];
// ファイルオープン
fp = fopen(argv[1],"r"); //ファイル名を引数argv[1]で指定
if(fp == NULL){
fprintf(stderr,"Can't open %s.\n",argv[1]);
return EXIT_FAILURE;
}
// ファイル読み込み
while(fscanf(fp, "%s", buff) != EOF){
fprintf(stdout, "%s", buff);
}
puts("");
// ファイルクローズ
fclose(fp);
return 0;
}
バイナリファイルの読み込み・書き込み
バイナリファイルとテキストファイル
ファイルの読み書きにはテキストモードとバイナリモードの2つのモードがあります。テキストモードでは文字、数値、記号などをすべてASCIIやシフトJISなどの文字コードとして保存します。一方、バイナリモードでは、そのままのデータ形式で保存をします。
例えば整数123は、テキストモードではそれぞれの数字を文字コード00110001b、00110010b、00110011bとしてファイルに保存します。一方、バイナリファイルでは同じ整数123を32bit幅の整数型として扱うならば00000000000000000000000001111011bとして保存します。
※ バイナリエディタでファイルを開くと、保存されているデータをそのままの状態で確認することができます。
バイナリファイルの書き出し
整数値123をバイナリ型式で保存するプログラム例です。ファイルのオープンモード指定にはバイナリモードでの書き込みを示す"wb"を指定します。書き込みの関数にはfwriteを使います。引数には順に、変数名、変数の型のバイト数、ファイル識別子を指定します。
file_bwrite.c
#include <stdio.h>
int main(void)
{
FILE *fp; //ストリーム
int a = 123; //書き込むデータ
char filename[] = "sample.dat"; //ファイル名
fp = fopen(filename,"wb"); //ファイルオープン
fwrite(&a, sizeof(int), 1, fp); //変数a、int型サイズのデータ、1個分を、fpに書き込む
fclose(fp);
return 0;
}
sizeof演算子 変数のバイト数を取得する
入出力関数(バイナリモード)
fwrite関数 |
形 式 | fwrite(const void *ptr, size_t size, size_t n, FILE *fp) |
機 能 | ptrが示す領域からsizeバイト単位でn個のデータをストリームfpへ書き込む。 |
戻り値 | 成功:書き込みに成功した個数 失敗:0 |
fread関数 |
形 式 | fread(const void *ptr, size_t size, size_t n, FILE *fp) |
機 能 | ストリームfpからsize単位でn個のデータを読み込み、ptrが示す領域に格納する。 |
戻り値 | 成功:読み取り個数 失敗:0 |
ファイルオープンのモード指定(バイナリモード)
モード | 説明 | ファイルがある場合 | ファイルがない場合 |
rb | 読み込み専用 | 正常 | エラー |
wb | 書き込み専用 | 上書き | 新規作成 |
ab | 追加書き込み | 最後に追加 | 新規作成 |
rb+ | 読み込み・書き込み | 正常 | エラー |
wb+ | 書き込み・読み込み | 上書き | 新規作成 |
ab+ | 読み込みと追加書き込み | 最後に追加 | 新規作成 |
バイナリファイルの読み込み
バイナリファイルに保存されているint型整数を1つ読み込むプログラム例です。ファイルのオープンモード指定にはバイナリーモードでの読み込みを示す"rb"を指定します。読み込みの関数にはfwriteを使います。引数には順に、格納先変数名、変数の型のバイト数、データ数、ファイル識別子を指定します。
file_bread.c
#include <stdio.h>
int main(void)
{
FILE *fp;
int a;
char filename[] = "sample.dat";
if((fp = fopen(filename,"rb")) == NULL){
printf("Can't open %s.\n", filename);
}
else{
fread(&a, sizeof(int), 1, fp); //変数aに、int型サイズのデータを、1個分、fpから読み込む
printf("%d\n",a);
}
fclose(fp);
return 0;
}
配列データをバイナリファイルへ書き出す
以下は、整数値0から198までの偶数を配列に格納して、その配列をファイルに書き出すプログラム例です。
file_write_array.c
#include <stdio.h>
#define N 100
int main(void)
{
FILE *fp;
char filename[] = "sample.dat";
int m[N];
int i;
for(i=0; i<N; i++) m[i] = i*2; //配列に0〜198までの偶数を格納
fp = fopen(filename,"wb"); //ファイルオープン
fwrite(m, sizeof(int), N, fp); //配列の書き出し
fclose(fp); //ファイルクローズ
return 0;
}
配列データをバイナリファイルから読み込む
以下は、(3)でファイルに書き出した0〜198までの偶数の配列データを読み込み、各要素を表示するプログラム例です。
file_read_array.c
#include <stdio.h>
#define N 100
int main(void)
{
FILE *fp;
int m[N];
char filename[]="sample.dat";
int i;
if((fp = fopen(filename,"rb")) == NULL){ //ファイルオープン
printf("Can't open %s.\n", filename);
}
fread(m, sizeof(int), N, fp); //配列として読み込み
fclose(fp);
for(i=0; i<N; i++)
printf("m[%d]=%d\n",i,m[i]); //配列要素の表示
return 0;
}
ランダムアクセス
ファイル関連関数
ftell関数 |
形 式 | long ftll(FILE *fp) |
機 能 | ストリームの現在のファイル位置指示子の値を取得する。 |
戻り値 | 成功:ファイル位置表示子の値 失敗:-1 |
rewind関数 |
形 式 | void rewind(FILE *fp) |
機 能 | ファイル位置位置子を、ファイルの先頭に位置する。 |
戻り値 | |
fseek関数 |
形 式 | int fseek(FILE *fp, long offset, int whence) |
機 能 | ストリームfpのファイル指示子をwhenceを基点にoffsetバイト移動させる。whenceには、SEEK_SET(ファイルの先頭)、SEEK_CUR(現在位置),SEEK_END(ファイルの最後)のいずれかを指定 |
戻り値 | 成功:0 失敗:0以外 |
ファイル位置表示子の位置の表示
file_point.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp; //ストリーム
char filename[64] = "ku.txt";
long fsize;
if((fp=fopen(filename,"r")) == NULL){
fprintf(stderr, "Can't open %s.\n", filename);
return EXIT_FAILURE;
}
fsize = ftell(fp); //ファイル表示子をの位置取得
printf("初期位置 %ld バイト\n",ftell(fp));
fseek(fp,0,SEEK_END); //ファイルの最後に移動
printf("終端移動 %ld バイト\n",ftell(fp));
rewind(fp); //ファイルの先頭へ
printf("リワインド %ld バイト\n",ftell(fp));
fgetc(fp); //一文字読み取り
printf("1文字読取後 %ld バイト\n",ftell(fp));
fclose(fp);
return 0;
}
ファイル操作
rename関数 |
形 式 | int rename( const char *oldname, const char *newname ) |
機 能 | ファイル名、ディレクトリ名の変更 |
戻り値 | 成功:0 失敗:0以外 |
remove関数 |
形 式 | int remove( const char *filename ) |
機 能 | ファイルの削除 |
戻り値 | 成功:0 失敗:0以外 |
expand_lessBack to TOP