第 10 回 入力

本日の内容


10-1. 標準入出力(Standard I/O)

MS-DOS プロンプトやコマンドプロンプトでは、キーボードを押すと字が入力 され、画面に字が表示されます。 しかし、このキーボードや画面の関係を変えることが可能です。 例えば、プログラムの実行結果をファイルに残したり、プログラムにキーボー ドからではなくファイルの内容を入力したりできます。 コンピュータの入出力は基本的にはキーボードと画面が対応していますが、特 定のファイルに変更が可能になっています(この入出力の変更のことをリ ダイレクトと言います)。

標準出力

標準出力を変更するにはコマンドを入力した後、「> 出力ファイル名」 を付け足します。 例えば、ディレクトリの内容をファイルに保存するには次のようにします。

dir > file1

このようにするとディレクトリの内容は画面に表示されず、 file1 に保存さ れます。 echo コマンドは「画面に文字を出すコマンド」として紹介しましたが、 実際は標準出力に対して文字を出すコマンドです。 次のようにすると、 file2 に abc という内容が書かれます。

echo abc > file2

演習10-1

  1. コマンド verの出力をファイルに保存しなさい。
  2. コマンド dir /wの出力をファイルに保存しなさい。
  3. 次のプログラムの出力をファイルに保存しなさい。
    #include <stdio.h>
    main(){
      printf("Hello World!\n");
    }
    

標準入力

標準入力を変更するにはコマンドを入力した後、「< 入力ファイル名」を つけます。 例を説明する前に、次のファイルを作って下さい。ファイル名は file3 にして下さい。

3
1
2

このファイルを sort コマンドに入力するには次のようにします。

sort < file3
すると、画面に与えた数が小さい順に出力されます。

EOF

sort コマンドはファイルの内容を小さい順に並べるコマンドです。 ファイルの最後に一番小さい値が来たら、その値を先頭に表示する必要がある ため、 sort の処理はファイルを全て読み終えてからでないと実行できません。 sort に限らず、ファイルを処理するプログラムはファイルの終りを知る必要 があります。 そのため、 OS はファイルが終ると EOF記号(End of File)という特殊な記号 (番兵)をプログラムに与えます。これにより、プログラムは ファイルが終ったことを知ることができます。 MS-DOS や Windows では Ctrl-Z という記号になります。 例えば、次のようにすると、 sort コマンドはキーボードから入力した数字を 並べ替えます。

例
sort[Enter]
3[Enter]
1[Enter]
2[Enter]
[Ctrl-Z][Enter]

その他

コマンドの出力をファイルに「追加」したい時は >> を使用 します。

例
echo abc > file1
echo def >> file1

コマンドの出力を、他のコマンドの入力にしたい時は |(縦棒) を使用しま す。このように複数のコマンドをつなげて処理することをパイプ処理 と言います。

例
dir | sort
dir | more
dir | sort | more

10-2. C 言語の独特の記法

式の値

C 言語では全ての文や式に値を持ちます。 例えば代入文は代入した数を値に持ちます。 j=0 という代入文の値は 0 です。 従って次のような記法が可能です。

i=j=0

この場合、「j=0」 により j に 0 が代入されますが、「j=0」の式の値は 0 なので、 i にはこの式の値 0 が代入されます。

条件文の式の値は、真であれば 1、偽であれば 0 です。従って、0==0 は 1、 1==0 は 0 の値を持ちます。一方、条件文では、 0 なら偽、0 以外の値なら 真だと判定します。したがって、次の文は同じ意味を持ちます。

if(x!=0){}
if(x){}

演習10-2

次の操作で変数 i,j の値はどうなっているか?

  1. i=(j=1)+1;
  2. i=((j=0)==0);
  3. if(i){i=0;}

代入演算子、インクリメント、デクリメント

プログラミングでは i=i+1 など変数の値を「増やす」など変化させる操作が 多いです。そのため、これを i+=1 と書くこともできるようになってます。 また、特に 1 を足したり引いたりすることが多いので、特別に 1 増やすのを ++i、 1 減らすのを --i と書くことができます。 これらのそれぞれの式の値は、計算結果の変数の値になります。 一方で、i++, i-- は変数の値を1 増やしたり減らしたり する際に、式の値は増減させる前の変数の値になります。

演習10-3

次のプログラムを実行し、実際に式の値を調べなさい。

#include <stdio.h>
main(){
  int i,j;
  printf("i=0 で i に 1 を増やす\n");
  i=0;j=(i=i+1);printf("i の値 %d, 式の値 %d\n",i,j);
  i=0;j=(i+=1);printf("i の値 %d, 式の値 %d\n",i,j);
  i=0;j=(++i);printf("i の値 %d, 式の値 %d\n",i,j);
  i=0;j=(i++);printf("i の値 %d, 式の値 %d\n",i,j);
}

i++ は次のように使います。

#include <stdio.h>
main(){
  char x[]="abcdefg";
  char y[]="hijklmn";
  char z[50];

  int i,j;
  i=0;
  j=0;
  while(x[j]!='\0'){
    z[i++]=x[j++];
  }
  j=0;
  while(y[j]!='\0'){
    z[i++]=y[j++];
  }
  z[i]='\0';
  printf("%s\n",z);
}

演習10-4

次のプログラムを完成させなさい。

#include <stdio.h>
main(){
  float x[]={3.1, 2.5, 6.3, 0};
  float s=0;
  int i=0;
  while(x[i]!=0){
    /* ここに代入演算子とインクリメントを使った文を一つだけ書く */
  }
  printf("合計 %f\n",s);
}
ヒント

代入演算子やインクリメントを使わない場合は次のようになります。

    s=s+x[i];
    i=i+1;

for 文

他の言語では for 文は特別な動作をしますが、 C 言語では for 文は while 文とほぼ同じです。 文法は次のようになります。

for(一番はじめにだけ実行する文 ; 条件文 ; 繰り返しの最後に実行する文 )
{ 繰り返しの内容}

つまり次の二つのプログラムは同じ意味になります。

  1. for(A;B;C){D;}
  2. A; while(B){D; C;}

実際には次のように使います。

for(i=0;i<10;++i){
  printf("%d\n",i);
}

これは次のプログラムと同じ意味です。

i=0;
while(i<10){
  printf("%d\n",i);
  ++i;
}

for 文で書くと、ループ変数の処理を一か所にまとめることができるので、繰 り返し回数が一目でわかるなどの利点があります。 逆に for の中にループ変数以外の文を多く入れてしまうと読みづらくなりま す。

演習10-5

次のプログラムの for 文を while 文に直しなさい。

#include <stdio.h>
#define N 3
main(){
  int i;
  float x[N]={3.2, 1.2, 5.4};

  for(i=0; i<N; ++i){
    printf("x[%d]=%f\n",i,x[i]);
  }
}

演習10-6

次のプログラムの while 文を for 文に直しなさい。

#include <stdio.h>
main(){
  int i;
  char x[]="abcdefg";

  i=0;
  while(x[i]!='\0'){
    printf("%c\n",x[i]);
    ++i;
  }
}

10-3. 論理演算

条件 A と B があった時、その両方が成立している時だけ文 X を実行するに は次のようにします。

if(A){
  if(B){
    X;
  }
}

一方、条件 A と B のどちらか一方が成立していれば X を実行するには次の ようにします。

if(A){
  X;
}else{
  if(B){
    X;
  }
}

このように if 文であれば複数の条件の組合せを処理することができます。 しかし、 while 文ではどうでしょうか? while 文では、文を複数繰り返さなければならないので、この if 文のような ことは基本的にできません。 そのため、複数の条件文の組合せを一つの条件文にまとめる必要があります。

C 言語において、条件 A と B に対して、両方が成立した時にだけ成立するこ とを表すには (A)&&(B) と書きます。 「a が 0 で b も 0 」という条件文を表すには (a==0)&&(b==0) と書きます。 これは一般にはAND 演算と呼ばれてます。

ABA AND B
成立成立成立
不成立成立不成立
成立不成立不成立
不成立不成立不成立

一方、C 言語において、条件 A と B に対して、どちらか一方が成立すれば成 立することを表すには (A)||(B) と書きます。 「a または b のいずれかが 0 」という条件文を表すには (a==0)||(b==0) と書きます。 これは一般にはOR 演算と呼ばれてます。

ABA OR B
成立成立成立
不成立成立成立
成立不成立成立
不成立不成立不成立

さらに、C 言語において、条件 A に対して、A が不成立のときだけ成 立することを表すには !(A) と書きます。 「a が 0 でない時」は a!=0 とも書けますが、!(a==0) とも書けます。 これは一般にはNOT 演算と呼ばれてます。

ANOT A
不成立成立
成立不成立

上記の例をもう一度とりあげます。 A と B の両方が成立している時 X を実 行するには次のように書きます。

if((A)&&(B)){ X; }

つまり、この表現を使うと、「A と B の両方が成立している時 X を繰り返す」 ということも次のように書くことができます。

while((A)&&(B)){ X; }

同じように「A と B のどちらか一方が成立している時 X を実行する」や「A と B のどちらか一方が成立している時 X を繰り返す」は次のように書けます。

if((A)||(B)){ X; }
while((A)||(B)){ X; }

演習10-7

方程式 a0+a1x=0 の解を求める下のプロ グラムを完成させなさい。 また、完成させたプログラムを用いて、次の方程式を解きなさい。

  1. 2x+1 = 0
  2. 5x+0 = 0
  3. 0x+3 = 0
  4. 0x+0 = 0
#include <stdio.h>
main(){
  float a[]={2,1};
  float x;

  printf("%fx+%f=0 の解は",a[1],a[0]);
  if(/* a[1] も a[0] も 0 */){
    printf("任意の値\n");
  }
  if(/* a[1] は 0 だが、 a[0] は 0 でない*/){
    printf("なし\n");
  }
  if(/* a[1] は 0 でない */){
    /* x の計算 */
   printf("%f\n",x);
  }
}

10-4. 入力

getchar

C 言語で標準入力から一文字得るには getchar() 関数を使います。 この関数を使用すると、入力された文字が文字型の値として得ることができま す。 つまり c=getchar(); とすると、一文字得ることができます。 ファイルの終りに達すると EOF という値になります。

ファイルの終りまで一文字ずつ読みながら処理をするには、一文字読む毎に EOF かどうか判定する必要があります。 つまり、プログラムは「(A)一文字読み、読んだ文字が EOF でないとき→(B) 読んだ文字を処理をする」ということ繰り返すことになります。 つまり概念的には次のようなプログラムになります。

while(一文字読み、読み込んだ文字が EOF でないとき){
  読んだ文字を処理する
}

while 文の条件は式の値を利用すると次のように書けます。

while((c=getchar())!=EOF){
  読み込んだ文字の入っている変数 c に対する処理
}

次は標準入力を標準出力にただ書き写すプログラムです。

#include <stdio.h>
main(){
  char c;
  while((c=getchar())!=EOF){
    printf("%c",c);
  }
}

なお、 MS-DOS プロンプトはキーボードから EOF(Ctrl-Z) を受けとると次の 出力文が無効になるというバグがあります。 入力を受けとり終ってから何か出力する際は、空の行を打つ printf 文が必要 になります。

演習10-8

次のプログラムを完成させ、ファイルの文字数を数えるようにしなさい。 また、このプログラムが何文字あるか、このプログラムの標準入力に与えて調 べなさい(つまり .\a.exe < このプログラム.c を行う)。

#include <stdio.h>
main(){
  char c;
  int n=0;
  while((c=getchar())!=EOF){
    /* 一文字受けとったら、 n を一増やす */
  }
  printf("\n"); /* MS-DOS プロンプトのバグ対策 */
  printf("合計 %d 文字\n",n);
}

演習10-9

ファイルの行数を数えるプログラムを書きなさい。 また書いたプログラムが何行あるか作ったプログラムで調べなさい。

ヒント

実は正確にやろうとすると難しい。 「改行の数を数える」という方法で妥協して良い。


演習10-10

変数 x に文字を与えておき、その文字がファイル中に何文字現れたかを数え るプログラムを書きなさい。


演習10-11

ファイルの最初から 5 行だけを表示するプログラムを作りなさい (ファイルが全部で 5 行以下の時にどうするかも考えなさい)。


演習10-12

ファイルに行番号をつけるプログラムを作りなさい。 例えば次のようなファイルがあったとします。

abc
def
ghi

このときこのファイルを入力すると次の出力が出るようにしなさい。

1: abc
2: def
3: ghi

(なお余裕があったら、ファイルが空の時に何も出力しないように工夫しなさ い)。


scanf

C 言語で変数に数値を入力するには scanf を使います。 使い方は printf とほぼ同様ですが、変数の指定では変数名の前に & 記 号を書きます(理由は詳しい本に譲ります)。 例えば変数 i に整数を入力する時は次のようにします。

scanf("%d",&i);

scanf は同時に複数の変数の値を得ることができます。 scanf は代入が成功した変数の数を値として返します(例えば、数字を入力さ せるに文字を入れられたら失敗します)。 また、ファイルの終りを検出すると EOF を返します。

次は入力した値を 2 倍して返すプログラムです。

#include <stdio.h>
main(){
  float x;
  int c;
  while((c=scanf("%f",&x))!=EOF){
    if(c==1){ /* x に値が代入された時だけ */
      printf("\t%f\n",2*x);
    }
  }
}

なお scanf は空白や改行を読み飛ばしますので、複数の数値を入力する時は、 適宜空白や改行で区切ります。 また、プログラムにキーボードから直接値を入力する時は、 Enter キーを押 すまではプログラムに値が渡らないことに注意して下さい。

演習10-13

ファイルの中の数を合計するプログラムを書きなさい。


演習10-14

ファイルの中の数の平均を計算するプログラムを書きなさい。


演習10-15

ファイルの中の数の最大値を出力するプログラムを書きなさい。


坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科