こんにちは、ナナです。
ファイルの書き方を学んだ次は、「ファイル」からの読み込み方法を学びましょう。
例えば、ファイルの中に書かれた名簿リストを、プログラムから読み込んで「名簿情報を加工したい」なんてことはよくあることですね。
このような時は「ファイル」という情報を、プログラム内のデータに読み込むことが必要となります。
本記事では次の疑問点を解消する内容となっています。
ファイルの開き方・閉じ方の基礎を知らない方は、まずは『C言語 ファイルの開き方・閉じ方【fopenとfcloseの使い方】』を見ておきましょう。
ファイルから読み込み:標準ライブラリ関数の種類
師匠!わかってますよ、「書き込み」の次は「読み込み」なんでしょ!わたしはファイルの中に書かれた情報を知りたいのですっ!
はい、その通り。「書き込み」と同じように「読み込み」にも同じように標準ライブラリ関数が用意されていますよ!
ファイルの情報を読み込むのも、標準ライブラリ関数を使えば簡単にできます。どんなものがあるのかを紹介しましょう!
ファイル読み込み:標準ライブラリ関数
代表的なファイル読み込みを行うための関数は、次のものです。
#include <stdio.h>
int fgetc(FILE * fp);
char * fgets(char * buf, int size, FILE * fp);
int fscanf(FILE * fp, const char * format, ...);
各関数には「file」を示す「f」の接頭語が付いているのがわかりますね。
そして、各関数の引数には、必ずファイルハンドルを示すFILE型ポインタ変数が存在します。
書き込み系の関数と同様に、引数には必ずファイルハンドルが用意されていますね。このルールは、必ず適用されるのです。
ファイル読み込み関数の特徴
各読み込み関数には、それぞれ特徴があります。用途に応じて使い分けましょう。
関数名 | 使い方 |
---|---|
fgetc | ファイルから1文字の読み込み |
fgets | ファイルから1行の文字列の読み込み |
fscanf | ファイルからフォーマット指定形式の文字列の読み込み |
これらの関数の使い方を順番に解説していきます。使いたい関数が決まっている方は、目次から記事を読み飛ばしてください!
前準備:ファイルを読み込み可能なモードで開くこと
師匠!書き込みと同じように、「ファイル」を開く時にはモード指定がやはり必要なんですか?
そうだね。今回はファイルからの「読み込み」をしたいから、モードは読み込みを示す「r」の指定が必要となるよ。
ファイルから読み込みをしたい時は、fopenをする際の「モード」に注意が必要です。「モード」とは、ファイルに対してどのような作業を行うかを示す情報です。
次のように、fopen_s関数にはモードに「r」を指定して開くようにしましょう。
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
// 読み込みモードでファイルを開く
fopen_s(&fp, "memo.txt", "r");
// ファイルから読み込み処理
// ・・・
fclose(fp);
return 0;
}
ファイルの開き方について詳しく知りたい方は『C言語 ファイルの開き方・閉じ方【fopenとfcloseの使い方】』を見ておくとよいでしょう。
ファイルを読み込む場合は、指定したファイルが存在している必要があります。テキストファイルを、あらかじめ作成しておく必要がありますよ!
fgetc関数を使ったファイルからの読み込み
それでは、一番簡単にできるファイルからの「読み込みの術」を教えてください!
それじゃあ、ファイルから1文字ずつ読み込むための「fgetc関数」から使ってみよう。
それでは扱いが簡単な「fgetc関数」から使い方を紹介しましょう!
fgetc関数の仕様
「fgetc関数」を使用すると、ファイルハンドルが示すファイルから1文字を読み込むことができます。
includeファイル | stdio.h |
関数仕様 | int fgetc(FILE * fp); |
引数1 | 文字を読み込みたいファイルハンドル |
戻り値 | ファイルから読み込んだ1文字 |
特記事項 | ファイルの終端まで読み込んだ場合は「EOF」が戻り値となる。 読み込み異常時も「EOF」が戻り値となる。 |
「fgetc」とは「file:ファイル」「get:取得する」「character:文字」を示しています。
fgetc関数を使ったサンプルプログラム
「fgetc関数」を使って、ファイルから文字を読み込んでみましょう!
注意してほしいこととして、「Hello.txt」をプロジェクトフォルダ内に事前に作成し、次の内容を書き込んでおいてください。
Hello
World
ファイルのオープンは「fopen_s関数」を利用したプログラム例となります。
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
char c;
// 読み込みモードでファイルを開く
fopen_s(&fp, "Hello.txt", "r");
// EOFまでファイルから文字を1文字ずつ読み込む
while ((c = fgetc(fp)) != EOF)
{
// 読み込んだ1文字を画面に出力する
printf("%c", c);
}
fclose(fp);
return 0;
}
読み込んだ文字を順番に画面に出力しています。実行すると画面には次の文字が表示されることでしょう。
Hello
World
実際にステップ実行で動かしながら、変数「c」の中身を見てみるとよいです。1文字ずつ取得できる様子が確認できますよ!
fgets関数を使ったファイルからの読み込み
師匠!「書き込み」と同じで、1文字ずつはしんどいんですよ。あるでしょ、一気にドカッと読み込むヤツが!
あるに決まってるんですよ。出し惜しみしないでください‼
あ、あるよ…、よくわかってるね。「fgets関数」だね。これで1行まとめてドカッと読み込めますよ!
「fgetc関数で1文字ずつ読み込むのは大変だ」そんなことを思われた方には、「fgets関数」を紹介しましょう。
fgets関数の仕様
「fgets関数」を使用すると、ファイルハンドルが示すファイルから1行の文字列を読み込むことができます。
includeファイル | stdio.h |
関数仕様 | char * fgets(char * buf, int size, FILE * fp); |
引数1 | 読み込んだ文字列を格納する場所へのポインタ |
引数2 | ファイルから読み込むサイズ。 |
引数3 | 文字列を読み込みたいファイルハンドル |
戻り値 | 正常に読み込めた場合は引数1のポインタ値が返却。 ファイル終端に到達した場合と読み込み異常時はNULLポインタが返却。 |
特記事項 | 改行コード(\n)が登場した場合は読み込みをそこで止める。 |
「fgets」とは「file:ファイル」「get:取得する」「string:文字列」を示しています。
fgets関数を使ったサンプルプログラム
「fgets関数」を使って、ファイルから文字列を読み込んでみましょう!
先ほどと同様に「num.txt」をプロジェクトフォルダ内に事前に作成し、次の内容を書き込んでおいてください。
1234567
89abc
「fgets関数」によってファイルから1行単位で読み込んで、その内容を画面に表示するプログラムです。
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
char moji[128];
// 読み込みモードでファイルを開く
fopen_s(&fp, "num.txt", "r");
// NULLポインタの終端までファイルから文字を1行ずつ読み込む
while (fgets(moji, 128, fp) != NULL)
{
// 読み込んだ1行を画面に出力する
printf("%s", moji);
}
fclose(fp);
return 0;
}
実行すると、ファイル内容がそのまま画面に表示されます。
1234567
89abc
fgets関数では1文字ずつしか読み込めませんでしたが、「fgets関数」であれば1行分の文字列をまとめて一気に読み込むことができます。
実際にステップ実行で動かすと、読み込んだ1行が「moji配列」の中に格納されているのがわかりますよ!
ファイル終端まで読み込むと、最後はNULLポインタが取得され読み込み処理が終了します。
「fgets関数」の読み込みサイズが1行より小さい場合の動き
fgets関数の第2引数は、ファイルからの読み込みサイズです。通常このサイズは、第1引数のメモリサイズを指定します。
もし、ファイルの中の1行の文字数よりも読み込みサイズが小さかった場合、どのような動きになるかを把握しておきましょう!
読み込みサイズに「4」を指定した場合の動きを確認してみましょう。
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
char moji[4];
int i;
// 読み込みモードでファイルを開く
fopen_s(&fp, "Hello.txt", "r");
// 読み込みサイズを「4」に指定
for (i=0; fgets(moji, 4, fp) != NULL; i++)
{
printf("%d回目 : %s\n", i, moji);
}
fclose(fp);
return 0;
}
このプログラムの実行結果は、次のものになります。
1回目 : 123
2回目 : 456
3回目 : 7
4回目 : 89a
5回目 : bc
この実行結果から、次のルールで動作しているのがわかります。
それではイメージ図でどのように読み込まれたのかを示しておきましょう。
3回目の読み込み後に1行の空白行があるのは「改行コード」を読み込んでいる影響が出ています。ちゃんと解析すると理由がわかりますね。
fscanf関数を使ったファイルからの読み込み
fgets関数があれば「ファイル」からガツッと中身を取得できるんですね~。よーし、これで読み込みは完璧ですよ!
ちょっと待って。実はファイル読み込みにおいて、すごく便利な関数がもう1つあるんだよ。それが「fscanf関数」なんだよ。
これは覚えておくとめちゃくちゃ便利な関数だから、絶対マスターしとこうね!
「fscanf関数」は、ファイル書き込みにおける「fprintf関数」の逆版です。この関数があれば、フォーマット形式で、文字列情報を変数に格納することができます。
このfscanf関数の便利さを是非、体験しておきましょう!
fscanf関数の仕様
「fscanf関数」を使用すると、ファイルからの文字列情報をフォーマット指定に従って変数に読み込むことができます。
includeファイル | stdio.h |
関数仕様 | int fscanf(FILE * fp, const char * format, …); |
引数1 | 文字列を読み込みたいファイルハンドル |
引数2 | ファイルから読み込むための入力フォーマット |
引数3以降 | 入力フォーマットで指定した読み込み先の変数を並べる |
戻り値 | 読み込めた項目の数。ファイル終端や異常時はEOFが返却される。 |
特記事項 | 第3引数以降は入力フォーマットと変数の型と数を合わせる必要がある。 |
入力フォーマットで指定できる、基本的な変換指定子は次のものです。
変換指定子 | 変数データ型 | 詳細 |
---|---|---|
%hhd | char unsigned char | 10進数で解釈し、1バイト変数へ格納する |
%hd | short unsigned short | 10進数で解釈し、2バイト変数へ格納する |
%d | int unsigned int | 10進数で解釈し、int型変数へ格納する |
%ld | long unsigned long | 10進数で解釈し、4バイト変数へ格納する |
%hhx | char unsigned char | 16進数で解釈し、1バイト変数へ格納する |
%hx | short unsigned short | 16進数で解釈し、2バイト変数へ格納する |
%x | int unsigned int | 16進数で解釈し、int型変数へ格納する |
%lx | long unsigned long | 16進数で解釈し、4バイト変数へ格納する |
%f | float | 単精度浮動小数点数で解釈し、float型変数へ格納する |
%lf | double | 倍精度浮動小数点数で解釈し、double型変数へ格納する |
%c | char | 1文字として解釈し、char型変数へ文字として格納する |
%s | char * | 文字列として解釈し、文字列を格納する |
%p | void * | 16進数の番地として解釈し、番地を格納する |
fscanf関数を使ったサンプルプログラム
fscanf関数を使って、書式を指定した任意の文字情報を、ファイルから読み込んでみましょう!
まずは、次のファイル「名簿リスト.txt」を事前にプロジェクトフォルダ内に作成しておきます。
山田太郎 32歳 172.5cm A型
高田次郎 24歳 185.3cm O型
佐藤三郎 63歳 153.8cm B型
それでは、このファイル情報から「氏名」「年齢」「身長」「血液型」を変数に取り込んでみましょう。年齢や身長は「文字」としてではなく、「数値」として取り込みますよ。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
char name[32] = { 0 }; // 氏名
int age = 0; // 年齢
double height = 0; // 身長
char blood = 0; // 血液型
// 読み込みモードでファイルを開く
fopen_s(&fp, "名簿リスト.txt", "r");
// ファイル終端まで1行ずつ変数へ読み込む
while (fscanf(fp, "%s %d歳 %lfcm %c型", name, &age, &height, &blood) != EOF)
{
// 取り出した情報を画面に表示
printf("%s %d %3.2lf %c\n", name, age, height, blood);
}
fclose(fp);
return 0;
}
変数へ読み込んだ結果を、ディスプレイに表示しており結果は次のものになります。
山田太郎 32 172.50 A
高田次郎 24 185.30 O
佐藤三郎 63 153.80 B
このように、「文字列」「整数」「浮動小数点」「文字」といった各変数に、文字列情報を読み込むことができるのが「fscanf関数」の特徴なのです。
ファイルの文字列が特定の決まったルールで記載されている場合は、非常に強力な読み込み処理が実現できます。csv形式のファイルを読み込むときは、すごく便利な関数です。
「fscanf関数」はファイル内の文字列から変数へ読み込むための関数ですが、「sscanf関数」はメモリ上の文字列から変数へ読み込むための関数です。
使い方はほとんど同じなため、より詳しく使い方を知りたい方は『C言語 sscanf関数【文字列を解析して変数へ:サンプル付き】』を読むとよいです。
Q&A:ファイルから読み込みに関するよくある質問
ファイルの読み込み方法で気になる疑問点はあるかな?
Q:fscanf関数を利用しようとしたらビルドエラーが発生しました。
fscanf関数を使おうとしたら、次のエラーが出たんです。どうしたらいいんですか?
error C4996: ‘fscanf’: This function or variable may be unsafe. Consider using fscanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
このエラーは「fscan関数」を使うのは危険だから、「fscanf_s関数」を使った方がいいよというアドバイスだね。
このエラーはいろいろな関数で発生するから、覚えておくといいよ。
このエラー内容に関して、本サイトの様々な記事で解説しております。
内容が気になる方は、fscanf関数の親戚であるsscanf関数の記事『C言語 sscanf関数【文字列を解析して変数へ:サンプル付き】』を読んでみてください。
理由と対策方法がわかりますよ。
課題:ファイルからの読み込み方が学べたかを確認しよう
課題1
課題内容
課題の前準備として、次のファイルをプロジェクトファイルに作成せよ。
Hello World!
このファイルをfopen関数を使って開き、「fgetc関数」を使って1文字ずつ文字を読み込み、英小文字を英大文字に変換して画面に表示せよ。
オープン関数 | モード | ファイル名 | 読み込み関数 |
---|---|---|---|
fopen_s | 読み込み | Hello.txt | fgetc |
出力期待結果が画面に表示されることを確認せよ。
出力期待結果
HELLO WORLD!
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
char moji;
fopen_s(&fp, "Hello.txt", "r");
while ((moji = fgetc(fp)) != EOF)
{
if (moji >= 'a' && moji <= 'z')
{
// 英小文字から大文字への変換
moji -= 0x20;
}
printf("%c", moji);
}
fclose(fp);
return 0;
}
ASCIIコードの規則性を利用すれば変換は簡単ですね。toupper関数を使う方法もありますよ。
『C言語 大文字・小文字の変換方法【自作する場合の考え方も解説】』を読めば使い方を理解できるでしょう。
課題2
課題内容
課題の前準備として、次のファイルをプロジェクトファイルに作成せよ。
The only way to do great work is to love what you do.
If you haven’t found it yet, keep looking.
Don’t settle. As with all matters of the heart, you’ll know when you find it.
このファイルをfopen関数を使って開き、「fgets関数」を使って1文字ずつ文字を読み込み、行番号を付与して画面に表示せよ。
オープン関数 | モード | ファイル名 | 読み込み関数 |
---|---|---|---|
fopen_s | 読み込み | SteveJobs.txt | fgets |
出力期待結果が画面に表示されることを確認せよ。
出力期待結果
1行目:The only way to do great work is to love what you do.
2行目:If you haven’t found it yet, keep looking.
3行目:Don’t settle. As with all matters of the heart, you’ll know when you find it.
#include <stdio.h>
#define D_STR_BUF_SIZE (128)
int main(void)
{
FILE * fp = NULL;
char moji[D_STR_BUF_SIZE] = { 0 };
int i;
fopen_s(&fp, "SteveJobs.txt", "r");
for (i=1; fgets(moji, D_STR_BUF_SIZE, fp) != NULL; i++)
{
printf("%2d行目:%s", i, moji);
}
fclose(fp);
return 0;
}
配列サイズはこのようのマクロ定義で実施することがあります。定数値を変えれば、全て連動して変更が掛かります。
課題3
課題内容
課題の前準備として、次のファイルをプロジェクトファイルに作成せよ。
錦織圭 , 1989年12月29日 , 178cm , 75.2kg
フェデラー , 1981年8月8日 , 185cm , 85.4kg
ジョコビッチ , 1987年5月22日 , 188cm , 77.7kg
大阪なおみ , 1997年10月16日 , 180cm , 69.5kg
このファイルをfopen関数を使って開き、「fscanf関数」を使って出力期待結果の形式にして画面に表示せよ。
ただし、各情報は次の変数に「fscanf関数」を使って取り込むこととする。
データ型 | 変数名 | 説明 |
---|---|---|
char | name[32] | 氏名 |
short | year | 誕生日:年 |
unsigned char | month | 誕生日:月 |
unsigned char | day | 誕生日:日 |
unsigned int | height | 身長(cm) |
double | weight | 体重(kg) |
出力期待結果
氏名:錦織圭 誕生日:1989.12.29 身長:178cm 体重:75.20kg
氏名:フェデラー 誕生日:1981.8.8 身長:185cm 体重:85.40kg
氏名:ジョコビッチ 誕生日:1987.5.22 身長:188cm 体重:77.70kg
氏名:大阪なおみ 誕生日:1997.10.16 身長:180cm 体重:69.50kg
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
char name[32] = { 0 }; // 氏名
short year = 0; // 年
unsigned char month = 0; // 月
unsigned char day = 0; // 日
unsigned int height = 0; // 身長
double weight = 0; // 体重
fopen_s(&fp, "player.txt", "r");
while (fscanf(fp, "%s , %hd年 %hhd月 %hhd日 , %dcm , %lfkg", name, &year, &month, &day, &height, &weight) != EOF)
{
printf("氏名:%s 誕生日:%d.%d.%d 身長:%dcm 体重:%3.2lfkg\n", name, year, month, day, height, weight);
}
fclose(fp);
return 0;
}
テキストファイルの内容に従って、書式を定義します。変数に取り込めさえすれば、後は自由に計算したり出力することができるようになります。