fgets() / fputs() / fgetc() / fputc()
| 対応: | C89(1989) |
|---|
テキストファイルを1行単位または1文字単位で読み書きする関数です。ログファイルの解析やテキストデータの加工によく使われます。
構文
// 1行をファイルから読み込みます(改行文字も含みます)。 // 戻り値: 成功時は buf、EOF またはエラー時は NULL。 char *fgets(char *buf, int size, FILE *stream); // 文字列をファイルへ書き込みます('\0' は書き込まれません)。 // 戻り値: 成功時は非負の値、エラー時は EOF。 int fputs(const char *str, FILE *stream); // 1文字をファイルから読み込みます。 // 戻り値: 読み込んだ文字(unsigned char として int にキャスト)、EOF はエラーまたは末尾。 int fgetc(FILE *stream); // 1文字をファイルへ書き込みます。 // 戻り値: 書き込んだ文字、エラー時は EOF。 int fputc(int c, FILE *stream);
関数一覧
| 関数 | 対象 | 概要 |
|---|---|---|
| fgets() | 行読み込み | 最大 size - 1 文字を読み込み末尾に '\0' を付加します。バッファオーバーフローを防げます。 |
| fputs() | 文字列書き込み | 文字列を書き込みます。改行は自動付加されないため必要なら手動で追加します。 |
| fgetc() | 文字読み込み | 1文字ずつ読み込みます。戻り値は int 型で、EOF(-1)と区別するために unsigned char 範囲の値を返します。 |
| fputc() | 文字書き込み | 1文字書き込みます。標準出力に書く場合は putchar() が使えます。 |
| getchar() | 標準入力 | fgetc(stdin) の省略形です。 |
| putchar() | 標準出力 | fputc(c, stdout) の省略形です。 |
サンプルコード
sample_fgets_fputs_fgetc_fputc.c
#include <stdio.h>
#include <string.h>
int main(void) {
// fputs でファイルに複数行書き込みます。
FILE *fp = fopen("lines.txt", "w");
if (fp == NULL) { perror("fopen"); return 1; }
fputs("Itadori Yuji\n", fp);
fputs("Fushiguro Megumi\n", fp);
fputs("Kugisaki Nobara\n", fp);
fclose(fp);
// fgets でファイルを1行ずつ読み込みます。
fp = fopen("lines.txt", "r");
if (fp == NULL) { perror("fopen"); return 1; }
char buf[256];
int line_num = 1;
while (fgets(buf, sizeof(buf), fp) != NULL) {
// fgets は改行文字を含むため、末尾の改行を除去します。
buf[strcspn(buf, "\n")] = '\0';
printf("%d: %s\n", line_num++, buf);
}
fclose(fp);
// 出力: 1: Itadori Yuji / 2: Fushiguro Megumi / 3: Kugisaki Nobara
// fgetc で1文字ずつ読み込み文字数を数えます。
fp = fopen("lines.txt", "r");
if (fp == NULL) { perror("fopen"); return 1; }
int ch, char_count = 0;
while ((ch = fgetc(fp)) != EOF) {
char_count++;
}
fclose(fp);
printf("文字数(改行含む): %d\n", char_count);
return 0;
}
gcc sample_fgets_fputs_fgetc_fputc.c -o sample_fgets_fputs_fgetc_fputc ./sample_fgets_fputs_fgetc_fputc 1: Itadori Yuji 2: Fushiguro Megumi 3: Kugisaki Nobara 文字数(改行含む): 48
fputc / fgetc で1文字ずつ変換する
fputc と fgetc を組み合わせると、ファイルを1文字ずつ処理できます。小文字を大文字に変換して別ファイルに書き込む例です。
fgetc_fputc_convert.c
#include <stdio.h>
#include <ctype.h>
int main(void) {
FILE *in = fopen("input.txt", "w");
if (in == NULL) { perror("fopen"); return 1; }
fputs("hello world\n", in);
fclose(in);
in = fopen("input.txt", "r");
FILE *out = fopen("output.txt", "w");
if (in == NULL || out == NULL) { perror("fopen"); return 1; }
int ch;
while ((ch = fgetc(in)) != EOF) {
fputc(toupper((unsigned char)ch), out);
}
fclose(in);
fclose(out);
out = fopen("output.txt", "r");
if (out == NULL) { perror("fopen"); return 1; }
char buf[64];
if (fgets(buf, sizeof(buf), out) != NULL) {
printf("変換後: %s", buf);
}
fclose(out);
return 0;
}
gcc fgetc_fputc_convert.c -o fgetc_fputc_convert ./fgetc_fputc_convert 変換後: HELLO WORLD
よくあるミス
よくあるミス1: gets() の使用
gets() はバッファサイズを指定できないため、長い入力でバッファオーバーフローが発生します。C11 で標準から削除されました。fgets() でバッファサイズを指定します。
fgets_safe.c
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[64];
if (fgets(buf, sizeof(buf), stdin) != NULL) {
buf[strcspn(buf, "\n")] = '\0';
printf("入力: %s\n", buf);
}
return 0;
}
よくあるミス2: fgetc の戻り値を char で受け取る
fgetc の戻り値を char 型で受け取ると、符号付き char 環境で EOF(-1)が 255 と混同され、ループが終わりません。
fgetc_type_ng.c
#include <stdio.h>
int main(void) {
FILE *fp = fopen("test.txt", "w");
if (fp == NULL) return 1;
fputs("hello", fp);
fclose(fp);
fp = fopen("test.txt", "r");
if (fp == NULL) return 1;
/* NG: char で受け取ると EOF を正しく検出できないことがある */
char ch_bad;
/* while ((ch_bad = fgetc(fp)) != EOF) { putchar(ch_bad); } */
/* OK: int で受け取る */
int ch;
while ((ch = fgetc(fp)) != EOF) {
putchar(ch);
}
putchar('\n');
fclose(fp);
return 0;
}
gcc fgetc_type_ng.c -o fgetc_type_ng ./fgetc_type_ng hello
概要
gets() は使用禁止です。バッファサイズを指定できないため、長い行を読み込むとバッファオーバーフローが発生します。代わりに必ずバッファサイズを指定できる『fgets()』を使用してください。
『fgetc()』の戻り値は必ず『int』型で受け取ってください。char 型で受け取ると、char が符号付きの環境では EOF(-1)が 255 と混同され、ループが終わらなくなります。
バイナリファイルの読み書きには改行変換が不要なため、『fread() / fwrite()』を使用してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。