こんにちは!フリーランスの長野です。
ファイルの読み込みって使ってますか?初期設定ファイルなどのファイルから読み込んで処理する機会は多いかと思います。
また空白文字やカンマ文字などの区切り文字で区切られたデータをファイルから読み込んで処理する機会も多いかと思います。これらの書式付きファイルを読み込む方法を知っておくと、コードの記述が簡潔に済む場合も多いので便利です。
この記事では、ファイルの読み込みについて
- ファイルの読み込み方法について
- 書式付きファイルの読み込み方法について
- 配列への格納方法について
など基本的な内容から、具体的な使い方の内容についても解説していきます。今回はファイルの読み込みについて、使い方をわかりやすく解説します!
ファイルの読み込み方法について
ファイルの読み込み方法について、順を追って説明していきます。
fopenの使い方について
ファイルを読み込むには、まずファイルを開く必要があります。
ファイルを開くにはfopen関数を使います。fopen関数の第1引数に読み込むファイルのパス名を入力し、第2引数に読み込むモードを入力します。
読み込みですので「”r”」と入力します。fopen関数の戻り値はFILE型の構造体の実体のアドレスになります。fopen関数はファイルの読み込みに失敗するとNULLを返します。
なお、Visual StudioのC++でコンパイルする際には注意が必要です。VisualC++2012よりfopen関数を使用するとコンパイルエラーが発生し、fopen_s関数を使うようにメッセージが表示されます。fopen_s関数はint型の戻り値を返し、ファイルの読み込みに成功すると0(ゼロ)を返し、失敗するとエラーコードを返します。
fopen_s関数は第1引数にFILE型構造体の実体のアドレス先の値を、第2引数には読み込むファイルのパス名を入力し、第3引数には読み込むモードを入力します。ファイルの読み込みが完了したらfclose関数を使ってファイルを閉じます。
それではサンプルコードを確認していきましょう。
#include <stdio.h> int main(void) { FILE *fp; // FILE型構造体 char fname[] = "test.txt"; fp = fopen(fname, "r"); // ファイルを開く。失敗するとNULLを返す。 if(fp == NULL) { printf("%s file not open!\n", fname); return -1; } else { printf("%s file opened!\n", fname); } fclose(fp); // ファイルを閉じる // C11で導入 VisualC++2012以降ではこちらのみ使用可能 errno_t err; // errno_t型(int型) err = fopen_s(&fp, fname, "r"); // ファイルを開く。失敗するとエラーコードを返す。 if(err != 0) { printf("%s file not open!\n", fname); return err; } else { printf("%s file opened!\n", fname); } fclose(fp); // ファイルを閉じる return 0; }
実行結果:
test.txt file opened! test.txt file opened!
このサンプルコードでは「text.txt」ファイルを読み込んでいます。あらかじめ「text.txt」ファイルをソースコードと同じ場所に用意しています。FILE型構造体の実体「fp」を宣言しています。ファイルの読み込みが失敗した場合の処理を記述しています。
fopen関数はファイルの読み込みに失敗するとNULLを返しますので、その場合の処理を記述しています。ファイルの読み込みが失敗した場合の処理は忘れずに記述するようにしましょう。
ファイルの読み込みが完了したら、fclose関数を使ってファイルを閉じるのも忘れないようにしましょう。
fopen_s関数も同じようにファイルの読み込みが失敗した場合の処理を記述し、ファイルの読み込みが完了するとfclose関数を使ってファイルを閉じています。
fgetcの使い方について
ファイルの読み込みができたら次はファイルの内容を読み込んでいきましょう。ファイルの内容を読み込む場合は文字列として読み込みます。
文字列を1文字ずつ読み込む方法と1行ずつ読み込む方法があります。まずは文字列を1文字ずつ読み込む方法を確認していきましょう。文字列を1文字ずつ読み込むにはfgetc関数を使用します。fgetc関数を使用するにはヘッダーファイル「stdlib.h」をインクルードする必要があります。
fgetc関数は引数にFILE型構造体の実体のアドレスをとります。fgetc関数は1文字ずつunsigned char 型として取り込み,戻り値にはint 型に変換して返します。fgetc関数は読み込みに失敗するとファイル終端文字(EOF)を返します。
それではサンプルコードを確認していきましょう。
test.txt:
Hello C! Hello World! Hello Tokyo!
#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp; // FILE型構造体 char fname[] = "test.txt"; int chr; fp = fopen(fname, "r"); // ファイルを開く。失敗するとNULLを返す。 if(fp == NULL) { printf("%s file not open!\n", fname); return -1; } while((chr = fgetc(fp)) != EOF) { putchar(chr); } fclose(fp); // ファイルを閉じる return 0; }
実行結果:
Hello C! Hello World! Hello Tokyo!
このサンプルコードでは「tesst.txt」ファイルを読み込んでいます。fgetc関数を使ってファイル終端文字(EOF)に到達するまで1文字ずつ取り出し、putchar関数で文字を書き出しています。
fgetsの使い方について
1行ずつ読み込む方法についても確認していきましょう。
1行ずつ読み込むにはfgets関数を使います。fgets関数を使用するにはヘッダーファイル「stdlib.h」をインクルードする必要があります。fgets関数は行の終端の改行文字まで読み込み次の行の処理へ移ります。
fgets関数の第1引数には1行分の文字列を格納する配列を、第2引数には1行の最大文字数を入力し、第3引数にFILE型構造体の実体のアドレスを入力します。戻り値には文字列のポインタを返し、失敗するとNULLを返します。
それではサンプルコードを確認しましょう。
test.txt:
Hello C! Hello World! Hello Tokyo!
#include <stdio.h> #include <stdlib.h> #define N 256 // 1行の最大文字数(バイト数) int main(void) { FILE *fp; // FILE型構造体 char fname[] = "test.txt"; char str[N]; fp = fopen(fname, "r"); // ファイルを開く。失敗するとNULLを返す。 if(fp == NULL) { printf("%s file not open!\n", fname); return -1; } while(fgets(str, N, fp) != NULL) { printf("%s", str); } fclose(fp); // ファイルを閉じる return 0; }
実行結果:
Hello C! Hello World! Hello Tokyo!
このサンプルコードでは「test.txt」ファイルを読み込んでいます。fgets関数を使って最大256バイトの文字列を読み込み表示しています。
書式付きファイルの読み込み方法について
読み込むファイルには空白やカンマで区切られたフォーマットのファイルがあります。
決まった形式で書かれたファイルから区切りごとにデータを読み込む方法についてみていきましょう。決まった形式で書かれたファイルを読み込むにはfscanf関数もしくはsscanf関数を使います。
それぞれの関数の使い方についてみていきましょう。
fscanfの使い方について
fscanf関数の第1引数にはFILE型構造体の実体のアドレスを、第2引数には読み込むフォーマット形式を入力し、第3引数以降には区切り文字で区切られたデータを格納するためのアドレスを入力します。
fscanf関数はint型の戻り値を返し、失敗するとファイル終端文字(EOF)を返します。
空白区切りファイルの読み込み方法について
空白区切りファイルを読み込む場合についてみていきましょう。
サンプルコードを確認していきます。
test.txt:
test01 1.0 1.1 1.2 1.3 1.4 test02 2.0 2.1 2.2 2.3 2.4 test03 3.0 3.1 3.2 3.3 3.4
#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp; // FILE型構造体 char fname[] = "test.txt"; char str[16]; float f1, f2, f3, f4, f5; fp = fopen(fname, "r"); // ファイルを開く。失敗するとNULLを返す。 if(fp == NULL) { printf("%s file not open!\n", fname); return -1; } while(fscanf(fp, "%s %f %f %f %f %f", str, &f1, &f2, &f3, &f4, &f5) != EOF) { printf("%s %.1f %.1f %.1f %.1f %.1f\n", str, f1, f2, f3, f4, f5); } fclose(fp); // ファイルを閉じる return 0; }
実行結果:
test01 1.0 1.1 1.2 1.3 1.4 test02 2.0 2.1 2.2 2.3 2.4 test03 3.0 3.1 3.2 3.3 3.4
このサンプルコードでは空白区切りのフォーマットで書かれた「test.txt」ファイルを読み込んでいます。fscanf関数の第2引数で空白区切りのフォーマットを記述し入力しています。
カンマ区切りファイルの読み込み方法について
カンマ区切りファイルの読み込みについても同じようにみていきましょう。
サンプルコードを確認します。
test.csv:
test01,1.0,1.1,1.2,1.3,1.4 test02,2.0,2.1,2.2,2.3,2.4 test03,3.0,3.1,3.2,3.3,3.4
#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp; // FILE型構造体 char fname[] = "test.csv"; char str[16]; float f1, f2, f3, f4, f5; fp = fopen(fname, "r"); // ファイルを開く。失敗するとNULLを返す。 if(fp == NULL) { printf("%s file not open!\n", fname); return -1; } while(fscanf(fp, "%[^,],%f,%f,%f,%f,%f", str, &f1, &f2, &f3, &f4, &f5) != EOF) { printf("%s,%.1f,%.1f,%.1f,%.1f,%.1f", str, f1, f2, f3, f4, f5); } fclose(fp); // ファイルを閉じる return 0; }
実行結果:
test01,1.0,1.1,1.2,1.3,1.4 test02,2.0,2.1,2.2,2.3,2.4 test03,3.0,3.1,3.2,3.3,3.4
このサンプルコードではカンマ区切りのフォーマットで書かれた「test.csv」ファイルを読み込んでいます。
fscanf関数の第2引数でカンマ区切りのフォーマットを記述し入力しています。書式の先頭データの「str」のように文字列のデータを扱う場合には注意が必要です。
区切り文字のカンマが文字列の一部と認識されるからです。ここでは正規表現[^](除外)を使って文字列に含まれるカンマを除外しています。
sscanfの使い方について
次にsscanf関数を使う方法について解説します。
fscanf関数と使い方はよく似ていますが、fgets関数で取得した1行の文字列を扱う点で違いがあります。sscanf関数の第1引数に処理する対象の文字列名を、第2変数には読み込むフォーマット形式を入力し、第3引数には区切り文字で区切られたデータを格納するためのアドレスを入力します。
それではサンプルコードで確認していきましょう。
test.txt:
test01 1.0 1.1 1.2 1.3 1.4 test02 2.0 2.1 2.2 2.3 2.4 test03 3.0 3.1 3.2 3.3 3.4
#include <stdio.h> #include <stdlib.h> #define N 256 // 1行の最大文字数(バイト数) int main(void) { FILE *fp; // FILE型構造体 char fname[] = "test.txt"; char line[N]; char str[16]; float f1, f2, f3, f4, f5; fp = fopen(fname, "r"); // ファイルを開く。失敗するとNULLを返す。 if(fp == NULL) { printf("%s file not open!\n", fname); return -1; } while(fgets(line, N, fp) != NULL) { sscanf(line, "%s %f %f %f %f %f", str, &f1, &f2, &f3, &f4, &f5); printf("%s %.1f %.1f %.1f %.1f %.1f\n", str, f1, f2, f3, f4, f5); } fclose(fp); // ファイルを閉じる return 0; }
実行結果:
test01 1.0 1.1 1.2 1.3 1.4 test02 2.0 2.1 2.2 2.3 2.4 test03 3.0 3.1 3.2 3.3 3.4
このサンプルコードでは空白区切りのフォーマットで書かれた「test.txt」ファイルを読み込んでいます。fscanf関数の第2引数で空白区切りのフォーマットを記述し入力しています。
配列への格納方法について
書式付きファイルから読み込んだデータを配列へ格納する方法を説明します。
それではサンプルコードで確認していきましょう。
test.csv:
test01,1.0,1.1,1.2,1.3,1.4 test02,2.0,2.1,2.2,2.3,2.4 test03,3.0,3.1,3.2,3.3,3.4
#include <stdio.h> #include <stdlib.h> #define N 256 // 1行の最大文字数(バイト数) #define ROW 3 // 読み込むファイルのデータの行数 // 1列目の項目名と2列目以降のデータを格納する配列をメンバにもつ構造体 typedef struct str { char str[16]; float f_data[5]; } data; int main(void) { FILE *fp; // FILE型構造体 char fname[] = "test.csv"; char line[N]; char str[16]; float f1, f2, f3, f4, f5; int i = 0; data data[ROW]; fp = fopen(fname, "r"); // ファイルを開く。失敗するとNULLを返す。 if(fp == NULL) { printf("%s file not open!\n", fname); return -1; } while(fgets(line, N, fp) != NULL) { sscanf(line, "%[^,],%f,%f,%f,%f,%f", str, &f1, &f2, &f3, &f4, &f5); for(int j = 0; j < sizeof(data[i].str) / sizeof(data[i].str[0]); j++) { data[i].str[j] = str[j]; } float tmp[] = {f1, f2, f3, f4, f5}; for(int j = 0; j < sizeof(data[i].f_data) / sizeof(data[i].f_data[0]); j++) { data[i].f_data[j] = tmp[j]; } printf("%s,%.1f,%.1f,%.1f,%.1f,%.1f\n", data[i].str, data[i].f_data[0], data[i].f_data[1], data[i].f_data[2], data[i].f_data[3], data[i].f_data[4]); i++; } fclose(fp); // ファイルを閉じる return 0; }
実行結果:
test01,1.0,1.1,1.2,1.3,1.4 test02,2.0,2.1,2.2,2.3,2.4 test03,3.0,3.1,3.2,3.3,3.4
このサンプルコードではカンマ区切りのフォーマットで書かれた「test.csv」ファイルを読み込んでいます。
「test.csv」ファイルではカンマで区切られたデータの1列目に文字列が、2列目以降は浮動点付き小数が並んでいます。同じフォーマットで1行ずつ並んでいますので、1行のデータごとに配列に格納します。
1行のデータは文字列と浮動点付き少数が混じっていますので、文字列と浮動点付き少数の配列をメンバに持つ構造体を使っています。ファイル全体のデータは構造体の配列として格納しています。
fscanf関数の第2引数でカンマ区切りのフォーマットを記述し入力しています。前述のように書式の先頭データの「str」は区切り文字のカンマが文字列の一部と認識されるため、正規表現[^](除外)を使って文字列に含まれるカンマを除外しています。
まとめ
ここでは、ファイルの読み込みについて説明しました。
空白区切りやカンマ区切りファイルのように書式付きファイルは書式を指定することで、1行の文字列から1個ずつデータを簡単に取り出すことができるので便利です。
ファイルを読み込む機会は多いと思われますので使いこなすことができるように、この記事を何度も参考にして下さいね!