第5回標準入出力(5月19日)

今日のポイント

■ 標準入力と標準出力

プログラムコードに関して

C言語のプログラムにおいて,デフォルトの入力は,キーボードであり, デフォルトの出力は,コンソール画面である.これらを標準入力と標準出力と いう.C言語のプログラミングでは,以下の記述を使用する:

第1回目から,毎回使用してきた fgets の説明書きは以下のようになっ ている(man fgets より引用):
GETS(3)             Linux Programmer's Manual             GETS(3)

名前
       fgetc, fgets, getc, getchar, gets, ungetc - 文字と文字列の
       入力

書式
       #include 
       char *fgets(char *s, int size, FILE *stream);

説明
       fgets() は size よりも1文字以上少ない文字を stream から 読
       み込み、 s で示されるバッファに書き込む。読み込みは EOF ま
       たは改行文字を読み込んだ後終わる。改行文字は読まれるとバッ
       ファに書き込まれる。 '\0' 文字がバッファの中の最後の文字の
       後に1文字書き込まれる。
返り値
       fgetc(), getc(), getchar() は文字を unsigned char として読
       んで, int にキャストして返すか、ファイルの終りを EOF と し
       て返すか、エラーを返す。

fgets は,ある入力からの文字入力を受け付ける.第3引数の FILE *stream が入力元である.標準入力を入力元とするときは,第3引数の値とし て,stdin を指定する.

fprintf の説明書きは以下のようになっている(man fprintfより引用):
       int fprintf(FILE *stream, const char *format, ...);

fprintf は,ある出力先にフォーマットに従って作られたテキストを出力 する.第1引数の FILE *stream が出力先である.標準出力に出力するときは, 第1引数の値として,stdout を指定する.

コンソール上の使用に関して

UNIX系での OS には,コンソールがある.コンソールは,コマンドを実行 する機能と,実行結果を画面に表示する機能がある.

コマンドを実行する機能は,「シェル」というインタフェースを介してい る.知能情報工学科の演習室のユーザの設定では,デフォルトで「bash(バッ シュ)」というシェルが使われている.「シェル」は,ファイルとコマンドの 間の情報のやりとりを支援する.

例題1

以下に,標準入力と標準出力を使用したプログラムの使用例を示す.
$ ./list0501 < f1.dat
23人分のデータを入力しました。
最高点は F.Alonso さんの 54 点でした。
平均点は 10.0 点でした。

データは,ここからダウンロードする.⇒ f1.dat

プログラムは,以下のとおり.
/* list0501.c */

#include <stdio.h>

#define SIZ 100

int main() {
  char line[SIZ], name[SIZ * 2];
  int s, p, max_s, max_p, tmp, num, sum;

  p = 0;               // 名前を読み込む位置
  max_s = -1;          // 最高点
  max_p = SIZ;         // 最高点の名前の位置
  name[max_p] = '\0';  // 最高点の名前を初期化
  num = 0;             // 入力した人数
  sum = 0;             // 合計点
  while( fgets( line, SIZ, stdin ) != NULL ) { // 標準入力から1行読み込み.
    sscanf( line, "%s %d", &name[p], &s);  // 名前と点を分ける.
    num += 1; // 入力した人数の加算
    sum += s; // 入力した得点の加算
    if( s > max_s ) { // もし最高点が入力されたならば,
      tmp = max_p; // 
      max_p = p;   // 名前を読み込む位置と最高点の名前の位置を swap
      p = tmp;     //
      max_s = s;   // 最高点の更新
    }
  }

  if( num != 0 ) { // 0 で割り算をしないために
    fprintf( stdout, "%d 人分のデータを入力しました。\n", num );
    fprintf( stdout, "最高点は %s さんの %d 点でした。\n", &name[max_p], max_s );
    fprintf( stdout, "平均点は %3.1lf 点でした。\n", (double)( sum / num ) );
  } else {
    fprintf( stdout, "入力がありません。\n" );
  }

  return 0;
}

■ パイプ

プログラムの実行結果をファイルに出力するのではなく,次に実行するプ ログラムの入力に直結させる機能が,シェルには用意されている.パイプ「|」を使う.

以下に使用例を示す.
$ grep ' 0' < f1.dat | sort -r

  1. f1.dat が grep コマンドの入力となっている.
  2. grep コマンドは,引数として ' 0'(スペースと0)が指定されているので,f1.datのうち,0 点の行のみを標準出力から出力する.
  3. パイプ記号があるので,grep の標準出力は,sort コマンドの標準入力と結び付く.
  4. sort コマンドは,標準入力からの入力を,アルファベットの降順(ABC とは逆順)にソートする.その結果は,標準出力に,すなわち,コンソール画面の上に出力される.

例題2

f1.dat から 0 点の行を抽出し,アルファベットの降順にソートした結果をファイル"f1_zero.dat" に格納せよ.
$ grep ' 0' < f1.dat | sort -r > f1_zero.dat

フィルタ

例題2からわかるように,パイプを使うとデータの加工が簡単に行える.データから特定の部分を抽出したり加工したりするプログラムをフィルタという.

課題

f1.dat から点数が,L点以上,U点未満となる行を抽出するプログラムを作成せよ(動作例を以下に示す).
$ ./a.out 10 20 < f1.dat
C.Klien 1
D.Coulthard 1
J.Villeneuve 6
M.Weber 6
N.Heidfeld 6
N.Rosberg 4
R.Barrichello 8
R.Schumacher 7

ヒント

/* prac0501.c */

#include 
#include 
#include 

#define SIZ 200

int main(int argc, char **argv){
  FILE *fin, *fout;
  char line[SIZ], name[SIZ];
  int  s, lower, upper;

  fin  = stdin;
  fout = stdout;

  if( argc != 3 ){
    fprintf( stderr, "抽出するスコアの範囲を指定してください\n");
    exit( 2 );
  }

  lower = // atoi 関数を用いて第1引数の文字列(argv[1])を数字に変換
  upper = // atoi 関数を用いて第2引数の文字列(argv[2])を数字に変換

  while( fgets( line, SIZ, fin) != NULL ){
    if( line[strlen(line) - 1] != '\n' ) {
      fprintf( stderr, "Too long message.\n");
      exit( 1 );
    }
    // line から名前と数字を抽出
    // もし,lower 以上,かつ,upper 未満ならば,
    // 標準出力から出力する
  }

  return 0;
}

□ 宿題(小レポート)

上記の課題を完成させて,1点以上20点未満の者を得点の降順にソートしてファイル f1_1_20.dat に出力するための実行方法,その実行結果,および,プログラムを示せ.実行時にはパイプと sort を使うこと.


(c) Masato TOKUHISA (2006, May 15)