テキストファイルを1行ずつ処理したい場面は、C言語の入門から実務まで非常によく登場します。
設定ファイルの解析、ログの読み取り、CSV風データの前処理など、用途は幅広いです。
しかし実際には、fgetsの使い方、改行の扱い、バッファサイズ、EOF判定、エラー処理など、つまずきやすい点が多くあります。
そこで本記事では、C言語でファイルを一行ずつ読み込む基本から、よくある失敗、安全な実装、実践コードまでを専門的に整理して解説します。
この記事でわかること
- 1行ずつ読むときの基本手順
- fgetsを使う理由と注意点
- 改行や長い行への対処方法
- 実務で使いやすい安全な書き方
C言語でファイルを一行ずつ読み込みする基本手順
C言語でファイルを一行ずつ読み込むときは、まずファイルを開き、その後に1行取得を繰り返し、最後に必ず閉じるという流れで実装します。
最も一般的で安全性の高い方法は、標準ライブラリの関数であるfgetsを使う書き方です。
1文字ずつ読む方法や、書式付き入力で処理する方法もありますが、行単位で扱いたいならfgetsが第一候補です。
なぜなら、読み込みサイズを制限でき、バッファあふれを避けやすく、改行を含めた1行をそのまま扱えるためです。
まずは全体像を押さえ、その後に細かな注意点を理解すると、安定したコードを書けるようになります。
一行ずつ読み込む処理の流れ
処理の基本は、fopenで開く → fgetsで繰り返し読む → fcloseで閉じるです。
この流れは短く見えますが、各段階で確認すべき点があります。特にfopenが失敗したときの分岐と、読み込みループの条件式は重要です。
代表的な形は次のようになります。
#include <stdio.h>
int main(void) {
FILE *fp;
char buffer[256];
fp = fopen("input.txt", "r");
if (fp == NULL) {
return 1;
}
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
printf("%s", buffer);
}
fclose(fp);
return 0;
}
このコードでは、1回のfgetsで1行分、または指定サイズに収まる範囲まで文字列を取得します。
ファイルの終わりに到達するか、読み込みエラーが起きるとNULLが返るため、ループ終了条件として自然に使えます。
なぜfgetsが基本になるのか
fgetsがよく使われる理由は、最大読み込み長を明示できることにあります。
C言語ではメモリ安全性を自分で意識する必要があるため、どこまで読み込むかを決められる関数は非常に扱いやすいです。
また、行の終わりに改行文字が含まれていれば、それも一緒にバッファへ格納されます。
この性質によって、行の区切りを保ったまま処理できる一方で、あとで改行を削除したい場面では追加の処理が必要になります。
つまり、fgetsは便利ですが、動作仕様を理解して使うことが大切です。
最初に覚えたい最小構成のサンプル
学習段階では、余計な処理を入れずに最小構成で挙動を確認するのがおすすめです。
たとえば、各行の先頭に行番号を付けて表示すると、何行読めているかが確認しやすくなります。
#include <stdio.h>
int main(void) {
FILE *fp = fopen("input.txt", "r");
char buffer[256];
int line_no = 1;
if (fp == NULL) {
perror("fopen");
return 1;
}
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
printf("%d: %s", line_no, buffer);
line_no++;
}
fclose(fp);
return 0;
}
この形を基準にすると、あとから改行除去、空行スキップ、長い行の判定などを段階的に追加できます。
最初から複雑にせず、動く最小形を起点にすることが、C言語では特に重要です。
fgetsを使うときの注意点とよくある失敗
1行ずつ読む処理は簡単に見えて、実際には細かな落とし穴があります。
特に多いのは、改行が残ることを想定していないケース、1行がバッファより長いケース、そしてEOFとエラーを混同するケースです。
これらを理解せずに実装すると、文字列比較が失敗したり、行が途中で分割されたり、原因不明の不具合が起きやすくなります。
読み込み自体よりも、読み込んだ後に何が入っているかを正しく把握することが、安定動作の鍵です。
改行文字が含まれる点に注意する
fgetsは、行末の改行文字が読み込める状況なら、その改行もバッファへ格納します。
たとえばファイルに hello と書かれている行を読むと、バッファには hello に続いて改行が入る場合があります。
そのため、文字列比較で hello と一致判定したいときに、そのまま比較すると失敗することがあります。
実務では、読み込んだ直後に末尾の改行を削除する処理を入れることがよくあります。
buffer[strcspn(buffer, "n")] = '';
この1行で、改行があればその位置を終端文字に置き換えられます。
見た目では気づきにくい不具合を防げるため、非常に実用的です。
バッファサイズより長い行は分割される
fgetsは、1行全体を必ず一度で読み込むわけではありません。
指定した配列サイズより長い行がある場合、1回では入り切らず、残りは次回のfgetsで読み込まれます。
つまり、1回のfgetsイコール必ず1行ではない点に注意が必要です。
行末に改行が入っていなければ、まだ同じ行の途中である可能性があります。
長い行を扱う可能性があるなら、改行の有無を見て続きを連結する設計も検討すべきです。
| 状況 | fgetsの結果 |
|---|---|
| 行が短い | 1回で1行を取得しやすい |
| 行が長い | 複数回に分割される可能性がある |
EOF判定とエラー判定を混同しない
fgetsがNULLを返したとき、それは必ずしも正常終了とは限りません。
ファイル終端まで読んだ場合もNULLですし、読み込みエラーが起きた場合もNULLです。
より厳密に扱うなら、ループ終了後にファイル終端かエラーかを確認します。
終端確認にはfeof、エラー確認にはferrorを使います。
基礎学習では省略されることもありますが、信頼性が求められる処理では区別できるようにしておくと安心です。
一行ずつ読み込みした後の実践処理
ファイルを1行ずつ読めるようになったら、次はその行をどう扱うかが重要になります。
実際の開発では、表示するだけで終わることは少なく、空行を飛ばす、区切り文字で分解する、条件に合う行だけ抽出するといった処理が必要です。
この段階で大切なのは、読み込みと解析を混ぜすぎないことです。
まず1行を安全に取得し、その後に整形や判定を行うと、保守しやすいコードになります。
行単位処理は多くのテキストデータで応用できるため、ここを理解すると実践力が大きく上がります。
空行や不要な行をスキップする方法
設定ファイルやログでは、空行やコメント行を除外したいことがよくあります。
その場合は、読み込んだ直後に改行を除去し、文字列の長さや先頭文字を確認すると判定しやすくなります。
たとえば、長さが0なら空行、先頭が特定記号ならコメント行として無視できます。
この前処理を入れるだけで、その後の解析処理がかなり簡潔になります。
入力データが常にきれいとは限らないため、最初に不要行を弾く設計は非常に有効です。
区切り文字で分割してデータを扱う
1行ずつ読み込んだデータは、さらに単語や項目に分解して使うことが多いです。
たとえば、空白区切りやカンマ区切りのデータを扱う場合、行を読み込んでから分割処理を行います。
C言語では文字列操作関数を使って分割できますが、元の文字列が書き換わるものもあるため注意が必要です。
特に簡易的なCSV風データなら処理できますが、引用符や埋め込み区切り文字を含む本格的なCSVは、単純分割だけでは不十分な場合があります。
用途に応じて、行の読み込みと項目解析を分けて考えると設計しやすいです。
行番号付きでエラー箇所を特定する考え方
実践では、どの行に問題があるかを示せると調査が一気に楽になります。
そのため、読み込みループで行番号を管理し、異常データが見つかったらその番号と内容を表示する実装が有効です。
たとえば数値が必要な項目に文字列が入っていた場合、何行目で失敗したのかが分かれば修正しやすくなります。
単に読み込みに成功したかだけでなく、入力品質を検査する視点を持つことで、行単位処理はより実務向きになります。
C言語で安全にファイル読み込みを行うコツ
C言語では、同じ処理でも安全性を意識した書き方と、そうでない書き方で品質に大きな差が出ます。
特にファイル処理は外部入力を扱うため、想定外の長さや形式に備えることが重要です。
安全な実装の基本は、固定長バッファの限界を理解し、失敗時の分岐を省略せず、読み込み後の文字列状態を明確にすることです。
派手な技巧より、堅実な基本の積み重ねが結果として最も強いコードにつながります。
fopenの失敗処理を必ず入れる
ファイルが存在しない、権限がない、パスが誤っているといった理由で、fopenは失敗することがあります。
そのままfpを使ってしまうと未定義動作につながるため、NULL判定は必須です。
学習用コードでは省略されることもありますが、実践では失敗時にエラーメッセージを出して終了する形が基本です。
入力ファイルを扱うコードは、正常系より異常系の作り込みが品質を左右します。
用途に合ったバッファ長を決める
バッファサイズは大きければ安心という単純な話ではありません。
必要以上に小さいと長い行を分割しやすくなりますが、根拠なく巨大にすると設計意図が曖昧になります。
扱うファイル形式に応じて、1行の最大長をある程度見積もることが大切です。
また、最大長が読みづらい入力を許容する必要があるなら、固定長バッファだけでなく、複数回読み込みを前提にした実装方針も検討するとよいです。
実務で押さえたいおすすめパターン
実務で使いやすいのは、1行を読み込むたびに改行を除去し、必要なら空行を飛ばし、行番号を進めながら処理する形です。
この流れは多くのテキスト入力で再利用しやすく、保守性も高くなります。
おすすめの流れ
- fopenでファイルを開く
- fgetsで1行取得する
- 改行を除去する
- 空行や不要行を判定する
- 解析や検証を行う
- fcloseで閉じる
この形をテンプレートとして持っておくと、設定ファイル、ログ、簡易データ一覧など、さまざまな場面で素早く対応できます。
まとめ
C言語でファイルを一行ずつ読み込むなら、基本はfgetsを使う方法です。
fopenで開き、fgetsで繰り返し取得し、最後にfcloseで閉じるという流れをまず確実に身につけることが重要です。
そのうえで、改行文字が残ること、長い行は分割されること、NULLが終端とエラーの両方を意味しうることを理解しておくと、実装の精度が大きく上がります。
さらに、空行スキップ、行番号管理、失敗時の分岐まで含めて考えれば、学習用を超えて実務でも通用するコードになります。
1行ずつ読む処理は、C言語の文字列処理とファイル処理の基礎が詰まった重要テーマです。
まずは最小構成のサンプルを動かし、次に改行除去や検証処理を追加しながら、堅実な実装へ発展させていくのがおすすめです。
コメント