標準入出力

■2-1 標準入出力

これまで、キーボードから文字や数字を入力する時にはscanf関数を、画面に文字や数字を出力する時はprintf関数を用いていましたが、ここで入出力についてもう少し詳しく説明していきます。そもそも、プログラムは「標準入力」から何かを読み込み、「標準出力」へ何かを出力します。そして、特に特別に指定がない場合に、標準入力がキーボード、標準出力がディスプレイと設定されているわけです。この「標準入力がキーボード」、「標準出力がディスプレイ」という指定を変更する方法はいくつか存在しますが、最も簡単な方法は、2-4で説明するリダイレクションです。これについては後述しますが、ここでは、プログラムには標準入力と標準出力があり、通常はそれぞれキーボードとディスプレイに対応するということを覚えておいてください。



■2-2 scanf関数以外の入力関数

初級編で学習した標準入力から文字や数字を読み込む関数にscanf関数がありました。ここでは、これ以外の入力関数についてみていきます。

1文字入力

標準入力から1文字だけを読み込むにはfgetc関数が使えます。

int fgetc(stdin)
この関数は、任意のファイルから1文字だけ読み込む関数(ファイルからの入力に関しては後で説明します)ですが、関数の引数をstdinとすると、ファイルからではなく標準入力から読み込むようになります。
使用例
#include <stdio.h>

main(){
  char a;

  printf("一文字入力してください:", a);
  a = fgetc(stdin);                /* 一文字入力 */

  printf("読み込まれた文字は%cです。\n", a);
}

このプログラムを実行すると以下のようになります。


演習問題2-1

下記のように続けて2文字を読み込み、それを表示するプログラムを作成せよ。

演習問題2-1の解答


1行入力

標準入力から1行(つまり、文字列)を読み込むにはfgets関数が使えます。

char *fgets(s, size, stdin);
      
ここで、sは入力された文字列を格納する場所を示すアドレスで、通常は文字配列のアドレスとなるでしょう。sizeは最大何文字をキーボードから読み込むかという指定です。
使用例
#include <stdio.h>

main(){
  char string[10];

  printf("文字列を入力してください:");
  fgets(string, 10, stdin);             /* 一行入力 */

  printf("読み込まれた文字列は%sです。\n", string);
}

この例では、キーボードから入力された文字を配列stringの中に格納しています。そのあと、画面にprintf関数を使って表示しているわけです。ここで、fgets関数が実際に読み込む文字数は「2番目の引数size- 1」であることに注意してください(-1の理由は文字列の最後には必ず"\0"が必要だからですね)。

つまり、ここでは配列stringは要素数が10の文字配列ですから、fgets関数の第2引数には10までしか指定できないということです。すなわち、100文字読み込みたいときは、fgets関数の第2引数に101を指定し、読込先である配列stringの要素数を101としなければならないということです。

なお、fgets関数も改行を含めて配列に読み込みますので、上のサンプルの実行画面は以下のようになります。理由は各自考えてみてください。



■2-3 printf関数以外の出力関数

ここでは、これまで使ってきたprintf関数以外の出力用関数について説明します。

1文字出力

標準出力に1文字出力する関数はputc関数です。

int fputc(c, stdin)
      
この関数は、任意のファイルに1文字だけ出力する関数(ファイルへの出力に関しては後で説明します)ですが、関数の第2引数をstdoutとすると、ファイルではなく標準出力へ出力します。関数の第1引数(c)は出力したい文字が入った変数、あるいは定数です。
使用例
#include <stdio.h>

main(){
  char a;

  printf("一文字入力してください:");
  a = fgetc(stdin);             /* 一文字入力 */

  fputc(a, stdout);             /* 文字変数が表示される */
  fputc('\n', stdout);          /* 文字定数も表示可能 */
}

この例では、fgetc関数を用いて読み込んだ文字をfputc関数を用いて表示しています。また例のように、第1引数に文字定数を指定することで、定数を表示することも出来ます。

1行出力

標準出力へ文字列を表示するにはfputs関数が使えます。

int *fputs(s, stdout);
      
ここで、sは出力したい文字列を示すアドレスです。
使用例
#include <stdio.h>

main(){
  char string[10];

  printf("文字列を入力してください:");
  fgets(string, 10, stdin);             /* 一行入力 */

  fputs("読み込まれた文字列は", stdout);
  fputs(string, stdout);
  fputs("です。\n", stdout);
}

この例は、先のfgets関数で読み込んだ文字列をfputs関数を用いて画面に表示しています。

このように、標準入出力に関する関数は、scanf関数やprintf関数以外にも幾つも存在します。では、どれを使えばよいか?となると、これは結構難しい問題です。

例えば、scanf関数はその仕様から、「空白文字そのものをキーボードから読み込むことが出来ない」という制限があります。そのため、キーボードから空白文字を読む必要があるときは、fgets関数などを用いることになるでしょう。それ以外については、適材適所としかいえませんが、その判断をするにはここまでの内容をきちんと理解していても無理があります。ここでは、「こういった関数もある」ということを覚えて、使い方をマスターしておいてください。



■2-4 リダイレクション

みなさんは、1-3の復習問題のプログラムを作った時に、同じような入力を何度もしましたよね?例えば、復習4のプログラムは、「二つの数字を入力してその数字の和、差などを計算せよ」というプログラムですが、プログラムの作成中にテストのために何度も実行を繰り返し、その度に「2」とか「3」とかの数字を入力したと思います。基本的にこれは必要な作業ですが、入力すべき数字が100個もあるようなプログラムの場合はどうでしょう?テストのたびに数字を100個も入れるなんてナンセンスですよね?

このような場合に、リダイレクションという方法があります。リダイレクションとは、「標準入出力をキーボードやディスプレイ以外のものに切り替える」というものです。つまり、「標準入力をキーボードではなく、ファイルに変更する」といったことができるわけです。これを利用するれば、一度ファイルを作っておくことにより何度も同じ入力をする必要がなくなります。

リダイレクションの利用法(標準入力を変更する)

復習4のプログラムを実行する場合を例にとって説明していきます。
まず、xemacsなどのエディタを用いて、10 改行 20 改行 という内容の以下のようなファイルnyuryokuを作成します。

つぎに、./enzan1 < nyuryoku 改行と入力すると、ファイルnyuryoku中の10と20があたかもキーボードから入力されたようにプログラムが動きます。これが、リダイレクションで、標準入力(キーボード)がファイルnyuryokuに切り替わったことになります。なお、このファイル名は任意に決めることが可能です。あとは、テストのための実行のたびに、./enzan1 < nyuryoku 改行と入力すればいいわけです。



リダイレクションを用いて、「標準出力をファイルに切り替える」こともできます。これは、レポートの提出などで、プログラムの実行結果を印刷したり、メールで提出しないといけない、といったときに用います。これのイメージは、「画面に表示される代わりに、ファイルに出力される」というものです。

リダイレクションの利用法(標準出力を変更する)

復習4のプログラムを実行する場合を例にとって説明していきます。
プログラムの表示を画面ではなく、kekkaというファイルにに保存したい場合は、./enzan > kekka 改行と入力します。ファイル名は任意に指定できます。以下の例では、ファイルkekkaの内容をcatコマンドを使って表示しています。


これら、二つのリダイレクションは同時に指定することも出来ます。下の例では、キーボードから入力する変わりにファイルnyuryokuから文字を読み込み、その結果を画面に出力する代わりにファイルkekka2へ保存しています。



■2-5 標準エラー出力

2-4で説明したリダイレクションは便利なもので、プログラムのテスト実行以外にも、複数のプログラムをつなげて処理をするといったときにも多用されます(UNIXのコマンドラインの文化というヤツです...)。しかし、このままでは問題が発生することもあります。

例えば、「たとえ、標準出力を画面からファイルに切り替えていたとしても、プログラムが異常動作をしたときなど、絶対に画面に表示したい情報がある」という場合です。これは、プログラムのエラーメッセージなどの出力にあたります。プログラムの実行中に不具合が発生した時はユーザに早急に通知したいのに、エラーメッセージまでファイルに保存していてはユーザは気がつかないですよね。このような場合に対処するために「標準エラー出力」なるものがあります。これは、標準出力とは違って、2-4で説明したリダイレクションでは画面以外に切り替わらない出力です(ちなみに、標準エラー出力をリダイレクションする方法も存在しますが,ここでは説明しません。各自調べてみてください)。

標準エラー出力に文字などを出力するには、fputc関数やfputs関数を用いるときに、stdoutの代わりにstderrを指定すればOKです。

標準エラー出力の利用例

#include <stdio.h>

main(){

  fputs("ここは標準出力", stdout);
  fputs("ここは標準エラー出力", stderr);
}

このプログラムを、標準出力をファイルkekkaにリダイレクションして実行してみましょう。リダイレクションして実行したにもかかわらず、標準出力へ出力された文字列はファイルkekkaに保存されていますが、標準エラー出力に出力された文字列は画面に表示されていますよね。