こんにちは、ナナです。
ファイルの開き方と閉じ方を学んだ次は、「ファイル」への書き込み方法を学びましょう。
皆さんは、キーボードからファイルに対して様々な情報を書き込みながら保管していますね。
もし、その作業をプログラムからできたら便利な場面があるかもしれません。
本記事では次の疑問点を解消する内容となっています。
それでは、「ファイル」へのデータの書き込み方を学びましょう。
ファイルの開き方・閉じ方の基礎を知らない方は、まずは『C言語 ファイルの開き方・閉じ方【fopenとfcloseの使い方】』を見ておきましょう。
ファイルへの書き込み:標準ライブラリ関数の種類
師匠!「ファイル」を開いたのは良いのですが、中身が空っぽなんです。これじゃファイルの意味がないんですっ!
「ファイル」に私の覚えた忍術リストを書き込みたいんですよ!ファイルに文字を書くためにはどうしたらいいんですか?
ファイルは読み書きしてなんぼだよね!まずは「ファイル」への書き込み方法を覚えよう!
標準ライブラリ関数を使えば、簡単にファイルに書き込むことができるよ。
「ファイル」への書き込み方法はいろいろと用意されています。まずは、どのような標準ライブラリ関数があるのかを知っておきましょう!
ファイル書き込み:標準ライブラリ関数
代表的なファイル書き込みを行うための関数は、次のものです。
#include <stdio.h>
int fputc(int c, FILE * fp);
int fputs(const char * str, FILE * fp);
int fprintf(FILE * fp, const char * format, ...);
各関数には「file」を示す「f」の接頭語が付いているのがわかりますね。
そして、各関数の引数には、必ずファイルハンドルを示すFILE型ポインタ変数が存在します。
ファイルを制御するために、必ず引数にハンドル引数が存在する。
これはハンドル系の関数における特徴なんです。絶対に覚えておきましょう!
ファイル書き込み関数の特徴
各書き込み関数には、それぞれ特徴があります。用途に応じて使い分けましょう。
関数名 | 使い方 |
---|---|
fputc | ファイルへの1文字指定の書き込み |
fputs | ファイルへの文字列指定の書き込み |
fprintf | ファイルへのフォーマット指定形式の文字列の書き込み |
これらの関数の使い方を順番に解説していきます。使いたい関数が決まっている方は、目次から記事を読み飛ばしてください!
前準備:ファイルを書き込み可能なモードで開くこと
師匠!では、さっそくですがファイルへの書き込み方の指導をお願いします。どんな技を繰り出せばよいのですか?
ファイル書き込み用の関数を呼び出せばいいんだけど、その前にファイルの開き方に注意が必要です。まずは、書き込む前の準備をしようね!
ファイルに書き込みをしたい時は、fopenをする際の「モード」に注意が必要です。「モード」とは、ファイルに対してどのような作業を行うかを示す情報です。
次のように、fopen_s関数にはモードに「w」を指定して開くようにしましょう。
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
// 書き込みモードでファイルを開く
fopen_s(&fp, "memo.txt", "w");
// ファイルへの書き込み処理
// ・・・
fclose(fp);
return 0;
}
ファイルの開き方について詳しく知りたい方は『C言語 ファイルの開き方・閉じ方【fopenとfcloseの使い方】』を見ておくとよいでしょう。
ファイルに対して書き込みを行う際には、fopen_s関数のモードに「w」「a」といった書き込み可能なモードを指定する必要があります。
fputc関数を使ったファイルへの書き込み
ファイルに書き込むための関数にはいろいろ種類があるんですね。扱いが簡単なものから、教えてください!
うん、それじゃあ「fputc関数」から覚えていこうね。
この関数を使うとファイルに1文字書き込むことができるんだよ。
それでは扱いが簡単な「fputc関数」から使い方を紹介しましょう!
fputc関数の仕様
「fputc関数」を使用すると、指定した1文字をファイルハンドルが示すファイルに書き込むことができます。
includeファイル | stdio.h |
関数仕様 | int fputc(int c, FILE * fp); |
引数1 | 書き込むための文字 |
引数2 | 書き込み対象のファイルハンドル |
戻り値 | 書き込んだ文字 |
特記事項 | 書き込み異常の場合は「EOF」を返す。 |
「fputc」とは「file:ファイル」「put:入れる」「character:文字」を示しています。
fputc関数を使ったサンプルプログラム
「fputc関数」を使って、ファイルに文字を書き込んでみましょう!
ファイルのオープンは「fopen_s関数」を利用したプログラム例となります。
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
fopen_s(&fp, "Hello.txt", "w");
fputc('H', fp);
fputc('e', fp);
fputc('l', fp);
fputc('l', fp);
fputc('o', fp);
fputc('\n', fp);
fclose(fp);
return 0;
}
実行するとプロジェクトフォルダ内に「Hello.txt」が作られます。ファイルを開くと、次のように「Hello(改行)」が書かれているのがわかるでしょう。
Hello
ファイルに対して書き込みを行う最も基礎的な関数が「fputc関数」です。まずは、この関数を使ってファイルへ書き込むという感覚を手に入れましょう!
fputs関数を使ったファイルへの書き込み
師匠!「fputc関数」で1文字ずつ書き込むって、地道な修行みたいです。基礎が大事だとはわかってはいますが、わたしはもっと派手なヤツがいいんですよっ。
もっと、もっとないんですか?ドカッと一撃必殺技的なヤツが!
うんうん。まぁ、確かに1文字ずつ書くって大変だよね。そんな君には「fputs関数」を紹介しましょう。
「fputc関数で1文字ずつ書くのは大変だ」そんなことを思われた方には、「fputs関数」を紹介しましょう。
fputs関数の仕様
「fputs関数」を使用すると、指定した文字列をファイルハンドルが示すファイルに書き込むことができます。
includeファイル | stdio.h |
関数仕様 | int fputs(const char * str, FILE * fp); |
引数1 | ファイルへ書き込みたい文字列へのポインタ |
引数2 | 文字列を書き込みたいファイルハンドル |
戻り値 | 正常に書き込みできたとき0を返却する |
特記事項 | 書き込みが失敗したときは「EOF」を戻り値で返す |
「fputs」とは「file:ファイル」「put:入れる」「string:文字列」を示しています。
fputs関数を使ったサンプルプログラム
「fputs関数」を使って、ファイルに文字列を書き込んでみましょう!
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
fopen_s(&fp, "Hello.txt", "w");
fputs("Hello\n", fp);
fclose(fp);
return 0;
}
Hello
fputc関数では1文字ずつしか書き込めませんでしたが、「fputs関数」であれば文字列をまとめて一気に書き込むことができます。
fputc関数よりfputs関数の方が使いやすいですね。文字列を一度に書き込めるのは、やっぱり便利なんです。
fprintf関数を使ったファイルへの書き込み
「fputs関数」を使うと、好きな文字列をファイルに書き込めて便利ですね~。でもでも、忍術リストは「忍術名」「習得難易度」「消費チャクラ」といった複数の情報で構成されているのです。
構造体で管理されたデータだから、文字や数字が入り乱れているのです。文字列にするのが大変なんです。
そんな時には、書式付きのファイル書き込みができる「fprintf関数」が便利ですよ。
これを使えば、複数の変数で構成された情報を好きなフォーマットでファイルに書き込めますよ。
printf関数のファイル出力版である「fprintf関数」は非常に便利な関数です。
fprintf関数の仕様
「fprintf関数」を使用すると、フォーマット指定した変数情報をファイルハンドルが示すファイルに書き込むことができます。
includeファイル | stdio.h |
関数仕様 | int fprintf(FILE * fp, const char * format, …); |
引数1 | 文字列を書き込みたいファイルハンドル |
引数2 | ファイルに出力したい出力フォーマット |
引数3以降 | 出力フォーマットで指定された変数を並べる |
戻り値 | 生成した文字列の文字数(ヌル文字を除く) |
特記事項 | 第3引数以降は出力フォーマットと変数の型と数を合わせる必要がある。 |
出力フォーマットで指定できる、基本的な変換指定子は次のものです。
変換指定子 | 変数のデータ型 | 説明 |
---|---|---|
%c | char | 1文字のアスキーコードを出力する |
%s | char * | 文字列を出力する |
%d | 整数型 | 10進数で出力する |
%x | 整数型 | 16進数(英小文字)で出力する |
%X | 整数型 | 16進数(英大文字)で出力する |
%f | 浮動小数点型 | 浮動小数点を出力する |
%p | ポインタ型 | 番地を16進数(英大文字)で出力する |
変換指定子を使うのがprintf系関数の特徴ですね。代表的な変換指定子は覚えておくとよいです。
fprintf関数を使ったサンプルプログラム
fprintf関数を使って、書式を指定した任意の文字情報を、ファイルに書き込んでみましょう!
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
char data1[] = "モノづくり";
char data2 = 'C';
int data3 = 2020;
fopen_s(&fp, "monozukuri.txt", "w");
// ファイルに指定フォーマットで文字列を生成
fprintf(fp, "%s %c 言語塾 %d年", data1, data2, data3);
fclose(fp);
return 0;
}
モノづくり C 言語塾 2020年
このように、「文字列」「文字」「数」といった様々な変数情報を、フォーマットに従いファイルへ出力することができます。
「fprintf関数」はファイルへの出力ですが、「sprintf関数」は文字列メモリへ出力する関数です。
使い方はほとんど同じなため、より詳しく使い方を知りたい方は『C言語 sprintfの使い方【複数の変数から文字列を作り出す方法】』を読むとよいです。
ファイルへの追記書き込み方法
師匠!大変です。私がせっかくファイルに書いておいた忍術リストが、書き込み関数を使ったら消えちゃったんです。
これでは、自分がどんな術を覚えたのか、わからなくなっちゃうじゃないですか!何とかしてください。
それは、ファイルを「a(追記)」モードで開いていないからだね。ファイルの開き方で動作が変わるから注意が必要だよ。
ファイルへの書き込みはモードとして「w(書き込み)」「a(追記)」の2つがあります。
指定したファイルがすでに存在している場合において「World」の文字を書き込もうとすると、モードの違いで次のように書き込み内容が変化します。
注意すべきなのは「w」を使用してファイルを開くと、前に書かれていたファイルの中身は破棄されてしまうことです。
現在のファイル内容に文字を追記したい場合は、モード「a」を指定して行いましょう。
追記モードによるファイル書き込みのサンプルコード
fopen_s関数の第3引数に「a」を指定することで、追記モードによるファイル制御が可能になります。
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
// 追記モードでファイルを開く
fopen_s(&fp, "Hello.txt", "a");
// Helloを書き込み
fputs("Hello\n", fp);
fclose(fp);
return 0;
}
Hello
Hello
Hello
このようにファイルを開く際に、新規で書き込みしたいのか、追記したいのかを決定しておく必要があります。
Q&A:ファイルの書き込みに関するよくある質問
ファイルの書き込みで、疑問点がある人は手を挙げて!
Q:「fputc関数」や「fputs関数」は異常時に「EOF」を返すとなっています。どんな時に返すんですか?
はーい。書き込みが失敗するケースで「EOF」が戻り値となるって話がありますが、そんなケースは具体的に何があるんですか?
あるあるって言いつつ、実はないやつでしょ!あるある詐欺ですよね?
そんなことないよ。「EOF(End Of File)」が返却されるケースは実在しますよ!
書き込みができない状況を再現すればよいわけですから、例えば、読み取り専用で開いたファイルハンドルに対してfputs関数を呼び出すと「EOF」が返ってきますね。
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
int ret;
fopen_s(&fp, "Hello.txt", "r");
// 読み取り専用ファイルへの書き込み
ret = fputs("Hello\n", fp);
if (ret == EOF)
{
printf("ERROR:書き込み失敗");
}
fclose(fp);
return 0;
}
ERROR:書き込み失敗
万が一失敗することがあって問題となる場合は、エラーチェックといった処理を入れておくのがよいでしょう。
課題:ファイルへの書き込み方が学べたかを確認しよう
課題1
課題内容
次のファイルをfopen関数を使って開き、出力期待結果の数列を「fputc関数」を使って構築せよ。
オープン関数 | モード | ファイル名 | 書き込み関数 |
---|---|---|---|
fopen_s | 書き込み | num.txt | fputc |
出力期待結果の内容が「num.txt」に書かれていることを確認せよ。
出力期待結果
1
22
333
4444
55555
666666
7777777
88888888
999999999
#include <stdio.h>
int main(void)
{
int i,j;
FILE * fp = NULL;
fopen_s(&fp, "num.txt", "w");
for (i=1; i<10; i++)
{
for (j=0; j < i; j++)
{
fputc('0' + i, fp);
}
fputc('\n', fp);
}
fclose(fp);
return 0;
}
fputc関数の引数は文字です。そのためアスキーコードの’0’を元に数値を数字に変換していますよ。
課題2
課題内容
次のプログラムをベースにfopen関数を使って開き、出力期待結果の数列を「fputs関数」を使って構築せよ。
オープン関数 | モード | ファイル名 | 書き込み関数 |
---|---|---|---|
fopen_s | 書き込み | 忍術.txt | fputs |
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
char * skillList[] = {"影分身", "豪火球の術", "名刺火炎手裏剣"};
return 0;
}
出力期待結果の内容が「忍術.txt」に書かれていることを確認せよ。
※改行文字は「fputc関数」を使って入れてよいものとする。
出力期待結果
影分身
豪火球の術
名刺火炎手裏剣
#include <stdio.h>
int main(void)
{
FILE * fp = NULL;
int i;
char * skillList[] = {"影分身", "豪火球の術", "名刺火炎手裏剣"};
fopen_s(&fp, "忍術.txt", "w");
for (i=0; i < sizeof(skillList) / sizeof(skillList[i]); i++)
{
fputs(skillList[i], fp);
fputc('\n', fp);
}
fclose(fp);
return 0;
}
fputs関数の引数は文字列へのポインタですよ。skill配列はポインタ配列になっているので、配列要素をそのまま渡せば整合が取れています。
課題3
課題内容
オープン関数 | モード | ファイル名 | 書き込み関数 |
---|---|---|---|
fopen_s | 書き込み | 習得忍術.txt | fprintf |
次のプログラムをベースに、構造体変数skillListで管理された忍術リストを、指定のフォーマットに従いファイルへ出力せよ。main関数の中を修正すること。
#include <stdio.h>
typedef struct
{
char name[32]; // 忍術名
char level; // 習得レベル「S」~「C」
double chakra; // 消費チャクラ量
} S_SKILL;
// 習得した忍術リスト
S_SKILL skillList[] =
{
{"影分身", 'A', 82.3},
{"豪火球の術", 'B', 43.5},
{"名刺火炎手裏剣", 'S', 130.8},
};
int main(void)
{
FILE * fp = NULL;
return 0;
}
出力フォーマットは、出力期待結果の内容から推測せよ。
出力期待結果
忍術名:影分身 習得難易度:A 消費チャクラ:82.3
忍術名:豪火球の術 習得難易度:B 消費チャクラ:43.5
忍術名:名刺火炎手裏剣 習得難易度:S 消費チャクラ:130.8
#include <stdio.h>
typedef struct
{
char name[32]; // 忍術名
char level; // 習得レベル「S」~「C」
double chakra; // 消費チャクラ量
} S_SKILL;
// 習得した忍術リスト
S_SKILL skillList[] =
{
{"影分身", 'A', 82.3},
{"豪火球の術", 'B', 43.5},
{"名刺火炎手裏剣", 'S', 130.8},
};
int main(void)
{
FILE * fp = NULL;
int i;
fopen_s(&fp, "習得忍術.txt", "w");
for (i=0; i < sizeof(skillList) / sizeof(skillList[i]); i++)
{
fprintf(fp, "忍術名:%s 習得難易度:%C 消費チャクラ:%.1lf\n",
skillList[i].name, skillList[i].level, skillList[i].chakra);
}
fclose(fp);
return 0;
}
「fprintf関数」による変換指定子を利用した出力方法はしっかりと習得しておきましょう。この関数さえあれば、目的の文字列を自由に作り出すことができるでしょう!