導入
fscanf は空白区切りの決まった形式を読むのに向いていますが、「1行まるごと」を読みたいとき(空白を含む文章や、行数が事前に分からないファイルなど)には fgets が向いています。
説明
fgets(buf, sizeof(buf), fp) は、fp から改行文字までの1行を(改行文字も含めて)配列 buf に読み込みます。第2引数に sizeof(buf) を渡すことで「バッファの大きさを超えて書き込まない」よう守ってくれます。ファイルの終端に達すると NULL を返すので、
while (fgets(buf, sizeof(buf), fp) != NULL) {
// 1行ずつの処理
}
という形が「ファイルを最後まで1行ずつ処理する」定番のパターンです。
flowchart TB
start["fopen(path, \"r\")"] --> loop{"fgets(line, size, fp)\nがNULLでない?"}
loop -->|Yes| proc["1行処理して表示"] --> loop
loop -->|No(ファイル終端)| close["fclose(fp)"]
#include <stdio.h>
int main(void) {
FILE *fp = fopen("/home/user/diary.txt", "w");
if (fp == NULL) {
printf("書き込み用に開けませんでした\n");
return 1;
}
fputs("今日は晴れ\n", fp);
fputs("公園を散歩した\n", fp);
fputs("夜はカレーを食べた\n", fp);
fclose(fp);
fp = fopen("/home/user/diary.txt", "r");
if (fp == NULL) {
printf("読み込み用に開けませんでした\n");
return 1;
}
char line[100];
int lineNo = 1;
while (fgets(line, sizeof(line), fp) != NULL) {
printf("%d行目: %s", lineNo, line);
lineNo++;
}
fclose(fp);
return 0;
}
fgets が読み込んだ line には改行文字 \n がそのまま残っています。だから printf("%d行目: %s", lineNo, line); では line の後ろに \n を追加していません(すでに含まれているため)。
やってみよう
日記の行を増やしたり内容を変えたりしてみましょう。char line[100] を char line[5] のように小さくすると、1行が途中で分断されて複数回に分けて読み込まれる様子も確認してみてください(バッファが小さいとどうなるかという実験です)。
演習
好きな3つの単語を1行に1つずつ fputs でファイルに書き込み、fgets で1行ずつ読み込んで「1: 単語」のような形式(行番号付き)で表示してください。
ヒント1を見る
fputs("りんご\n", fp); のように3回書き込みます。
ヒント2を見る
読み込みは int n = 1; while (fgets(line, sizeof(line), fp) != NULL) { printf("%d: %s", n, line); n++; }。