C言語基礎|ファイルとストリーム

「画面もキーボードもファイルも、ぜんぶ“同じ流れ”で扱える。」
Cの入出力がスッと分かる、ストリーム入門!

プログラムで計算した結果や、作った文字列―せっかく頑張って作ったのに、実行が終わった瞬間に消えてしまうのは、ちょっと切ないですよね。
そこで登場するのがファイル処理です。ファイルに保存しておけば、次に起動したときも読み戻せますし、別のプログラムや人とも共有できます。

そしてC言語の面白いところは、画面・キーボード・ファイル・プリンタ…みたいに相手が違っても、入出力を「流れ(ストリーム)」として同じ考え方で扱える点です。
この節では、まずその“共通の見方”をしっかり掴んでいきましょう。

ストリームってなに?

ストリームはひとことで言うと、文字やバイトが一方向に流れていく通り道です。
川の水みたいに「上流→下流へ流れる」と思うとイメージしやすいです。

たとえば、

  • キーボードから入力した文字がプログラムへ流れ込む。
  • プログラムが作った文字が画面へ流れて出ていく。
  • エラーメッセージだけ別ルートで画面へ出す。

…みたいな流れが、ストリームで表現できます。

図でつかむ:stdin / stdout / stderr

まずは“いつもの入出力”がどうなっているか、図で見てみます。

図:3つの標準ストリーム(イメージ)

この図のポイントは、入力も出力も「ストリーム」という同じ形でつながっていることです。
プログラムは「キーボード」や「画面」を直接触っているというより、stdin / stdout / stderr という“流れ”に対して読み書きしている、という感覚になります。

標準ストリームを表で整理

標準ストリームは、Cプログラム開始時に最初から用意される3本のストリームです。

名前役割よくつながる先代表的に使う関数
stdin入力を読むキーボードscanf, getchar, fgets
stdout通常の出力を書く画面printf, puts, putchar
stderrエラー出力を書く画面(別扱い)fprintf, perror

表の説明(読みどころ)

  • stdin は「読む専用の入り口」っぽい存在です。入力系の関数がここから取り出します。
  • stdout は「いつもの表示」。printf の出力は基本ここへ流れます。
  • stderr は「エラー用の表示」。stdout と同じ画面に出ることも多いですが、別のストリームなので分けて扱えるのが重要です(ログやリダイレクトで真価を発揮します)。

サンプル:標準入力→計算→標準出力、そしてエラーはstderrへ

「半径を入力 → 面積を表示」するプログラム例です。

サンプルプログラム

プロジェクト名:chap13-1-1 ソースファイル名:chap13-1-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <windows.h>
#include <stdio.h>

int main(void)
{
    SetConsoleOutputCP(65001); // 出力をUTF-8に設定
    double r;

    printf("半径を入力してください: ");

    if (scanf("%lf", &r) != 1) {
        fprintf(stderr, "入力が数値として読めませんでした。\n");
        return 1;
    }

    if (r < 0) {
        fprintf(stderr, "半径は0以上にしてください。\n");
        return 1;
    }

    printf("半径%.2fの円の面積は%.2fです。\n", r, 3.14159 * r * r);
    return 0;
}

このプログラムで流れているもの

ユーザーが入力する "3.5"
   ↓(stdin)
scanf が読み取って r に入れる
   ↓(計算)
3.14159 * r * r
   ↓(stdout)
printf が結果を画面に出す

もし入力ミスや負の値なら
   ↓(stderr)
fprintf(stderr, ...) がエラー文を画面に出す

図の説明

  • 正常系:stdin から値が入り、stdout に結果が出ます。
  • 異常系:stdout ではなく stderr にエラーを出します。これを習慣にしておくと、後でログ分離がやりやすくなります。

登場する関数と書式(stdin / stdout / stderr を意識しよう)

ここでは、出てきた命令(関数)を「どのストリームに対して動くのか」も含めて整理します。

printf の書式と意味(主にstdoutへ)

項目内容
書式printf(書式文字列, 引数1, 引数2, ...);
何をする?文字列を整形して出力する(通常はstdoutへ)
よく使う場面画面表示、結果表示、案内メッセージ

補足
書式文字列の中に %d や %f などの変換指定子を書き、後ろの引数をその形で埋め込んで表示します。

scanf の書式と意味(主にstdinから読む)

項目内容
書式scanf(書式文字列, 変数のアドレス, ...);
何をする?入力を読み取り、変換して変数へ格納する(通常はstdinから)
返り値正しく読み取れた項目数

補足(ここ大事)

  • scanf に渡すのは「変数そのもの」ではなく、変数のアドレスです(例:&r)。
  • 返り値を見れば、数値として読めたかどうかを判定できます。サンプルでは scanf(...) != 1 でチェックしています。

fprintf の書式と意味(任意のストリームへ出せる)

項目内容
書式fprintf(出力先ストリーム, 書式文字列, 引数...);
何をする?指定したストリームに整形して出力する
典型例fprintf(stderr, エラー文...);

補足
printf とほぼ同じ感覚で使えますが、最初に出力先(stdout や stderr など)を指定できるのがポイントです。

FILE型ってなに?(ストリームの“管理票”)

Cでは、ストリームは内部的にいろいろな管理情報を持っています。
その管理情報をまとめたものが FILE型(正確には FILE を指すポインタとして扱うことが多い)です。

FILE型が持つ代表的な管理情報(イメージ)

管理情報役割何のために必要?
ファイル位置表示子今どこを読んでいる/書いているか続きから読める、追記できる
エラー表示子エラーが起きたか入出力失敗を検出できる
ファイル終了表示子終端に達したかもう読めないことを判断できる

図:FILE型は「ストリームの状態」を持っている(概念図)

図の説明

  • プログラムがストリームへ読み書きすると、FILE型の中にある「位置」や「エラー」「終端」などの状態が更新されます。
  • つまりストリーム入出力は、ただ文字を流すだけじゃなく、状態を持ちながら進む処理なんですね。

まとめ:ストリームで考えると入出力が一気に統一される

  • 入出力は「相手ごとに別物」ではなく、ストリームという共通の流れで扱える
  • stdin / stdout / stderr の3本は、最初から用意される“標準ストリーム”
  • FILE型はストリームの管理情報(位置・エラー・終端など)を持つ
  • エラーは stderr に出す習慣をつけると、後で困りにくい

この考え方が入ると、次の「ファイルを開く」「ファイルに書く」「ファイルから読む」がとても自然につながっていきますよ。