入力(1)

本日の内容

    10-1. C言語のスタイル
    10-2. C 言語でのファイルの取扱い

10-1. C言語のスタイル

式の簡潔な書き方

sum=sum+a[i];

は次のように書くことができる。

sum+=a[i];

これは足し算だけでなく、引き算、かけ算、割り算なども同様である。

i-=2;
x*=3;
y/=2;

また、特に 1 増やす書き方、減らす書き方 は次のように書ける。

i+=1;
i++;
++i;

i-=1;
i--;
--i;

従って、以前取り上げた和を求めるプログラムは次のように書ける。

#include <stdio.h>
#define ASIZE 10
main(){
  int a[ASIZE]={4,5,3,1,7,8,9,2,0,6};
  int i,sum;

  sum=0;
  for(i=0;i<ASIZE;i++)
  {
    sum+=a[i];
  }
  printf("Sum=%d\n",sum);
}

また、式自体も値を持つ。 x=0;という式自体、 0 という値を持つので、

y=(x=0);

で、 y にも x にも 0 が代入される。

演習10-1

  1. 次のプログラムをコンパイルし実行しなさい。
  2. 上記の記法に書き直し、コンパイルし、実行し、結果が同じになることを確かめなさい。
#include <stdio.h>
main()
{
  int i,sum;

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

演習10-2

i++++iは意味が違う。 これを調べるため、プログラムを書いて確かめてみた。 次の問いに答えなさい。

  1. 下記のプログラムがどのような値になるのか予想しなさい。
  2. 実際にコンパイルし動かし、予想と実行結果を比べなさい。
  3. i++++iの違いを説明しなさい。
#include <stdio.h>
main()
{
  int i,j;

  i=0;
  j=(i=i+1);
  printf("i=%d, j=%d\n",i,j);
  j=(i+=1);
  printf("i=%d, j=%d\n",i,j);
  j=(i++);
  printf("i=%d, j=%d\n",i,j);
  j=(++i);
  printf("i=%d, j=%d\n",i,j);
}

else if

if 文の基本構文は次の通りである。

if(条件){
  文1
}else{
  文2
}

二つの条件,「条件1」,「条件2」があり、「条件1」が成立しないとき、「条件2」を調べたい時、基本的には次のように書く。

if(条件1){
  文1
}else{
  if(条件2){
    文2
  }else{
    文3
  }
}

これを次のようにも書ける。(但し、構文的な誤りを犯しやすいので、十分注意して使うこと)

if(条件1){
  文1
}else if(条件2){
  文2
}else{
  文3
}

演習10-3

  1. 次のブログラムを実行しなさい。
  2. 次のプログラムはなにをするプログラムか?
  3. else if の記法を使ってプログラムを書き換え、実行し、同じ動作になることを確かめなさい。
#include <stdio.h>
main()
{
  char a[]="100101";
  int i,x;

  x=0;
  for(i=0;a[i]!='\0';i++){
    if(a[i]=='0'){
      x*=2;
    }else{
      if(a[i]=='1'){
        x=x*2+1;
      }
    }
  }
  printf("%s(2) = %d(10)\n",a,x);
}

10-2. C 言語でのファイルの取扱い

標準入力、標準出力

UNIX や MS-DOS には標準入力、標準出力という特別なファイルが提供されている。 標準入力は通常はキーボードの入力であるが、他のファイルも指定できる。 また標準出力は通常は画面であるが、これも他のファイルを指定できる。 MS-DOS では標準入出力は CONというファイル名で参照できる。

標準入出力を変更することで、コマンドの入力にファイルを使用したり、コマンドの出力をファイルに保存したりできる。

  1. 標準出力のファイル指定方法
    コマンド > 標準出力の変更先
    
    例:
    echo abc
    画面に abc が出力される
    echo abc > def
    ファイル def に abc が出力される(type defで確認せよ)
  2. コマンド < 標準入力とするファイル名
    
    例:
    more
    キーボードに入力した文字を画面に表示する。但し、画面一枚ずつ区切る (Ctrl-Zを打ち、Enterで終了)
    more < def
    ファイル def の内容が画面に表示される。
  3. コマンド1の標準出力をコマンド2の標準入力に指定する。
    コマンド1 | コマンド2
    
    dir
    画面にディレクトリ内のファイル一覧が表示される。(ファイルが多いと、先に出力された行は画面の上に流れて消えていく)
    dir | more
    画面にディレクトリ内のファイル一覧が表示されるが、画面一枚ずつに区切られる。

C 言語での標準入力、標準出力

printf は標準出力に文字を書く関数である。 (stdio は「standard input/output」の意味。.h はヘッダファイルを区別するために付けられる)

以前作った hello.c を再度コンパイルする。

/* hello.c */
#include <stdio.h>
main(){
  printf("Hello, World!\n");
}
a.exe
画面に Hello, World! を出力する
a.exe > abc
ファイルabcHello, World!を出力する。 (type abcで確認せよ)

C 言語での文字の入出力

getchar()
標準入力から一文字読み込む
putchar(c)
標準出力に一文字書き込む (printf("%c",c) と同じ)

getchar()はファイルの終りを検出するとEOFという特殊な文字を読む

ファイルの内容をそのまま出力するプログラム

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

但し、C 言語風の書き方をするとこうなる(この書き方の理解は重要である)

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

演習10-4

上記のプログラムの名前をex10-1.cとする。

  1. このプログラムをコンパイルし、実行せよ。 実行すると、入力待ちになる。次のような入力を与えるとどのようになるか?
    abc(Enter)
    def(Enter)
    Ctrl-Z(Enter)(Ctrl ボタンを押しながら、Z を押す)
    
  2. a.exe < ex10-1.c
    

行の読み込み

テキストファイルを行ごとに処理したい時、「\n」で行の終りを判断する。

標準入力を行ごとに印刷する

考え方
  1. 以下を繰り返す。
    1. 文字を一文字ずつ読み込み、文字の配列変数に蓄えていく。
    2. もし、行の終りなら、配列変数を印刷し、はじめに戻る。
    3. もし、ファイルの終りなら、印刷して終了する。
素朴なプログラム
#include <stdio.h>
#define TRUE 1
#define BUFSIZE 1000
main()
{
  char line[BUFSIZE];
  int i;

  i=0;
  while(TRUE){            /* 無条件に繰り返す方法 */
    line[i]=getchar();
    if(line[i]=='\n'){
      line[i]='\0'; printf("%s\n",line);
      i=0;
    }else if(line[i]==EOF){
      line[i]='\0'; printf("%s\n",line);
      return;    /* プログラムを終了させるコマンド */
    }else{
      i++;
    }
  }
}
このプログラムの問題点
  1. プログラムの終りがプログラムの最後に来てないので、わかりづらい。
  2. 読む文字数が1000 文字を越えたらどうなるかわからない。(無限の長さの文字配列を作ることはできない)
  3. 変数 i がどういう変化をするのかわかりづらい。
流れを意識した考え方

「行を読む」という部分をひとまとまりにして流れを考える。 また配列変数が終ったら、それ以上配列にセットしない。

  1. 以下をファイルを読み終えるまで繰り返す
    1. 一行読む
    2. 行を印刷する
#include <stdio.h>
#define BUFSIZE 1000
main()
{
  char c;
  char line[BUFSIZE];
  int i;

  do{ /* do{ 文 } while(条件); に関しては後述 */
    i=0;
    c=getchar();
    while((c!=EOF)&&(c!='\n')){
      if(i<BUFSIZE-1){
        line[i++]=c; /* line[i] に c を入れた後、 i を一つ増やす */
      }
      c=getchar();
    }
    line[i]='\0';  /* 行の最後に文字列の終りの文字を入れる */

    printf("%s\n",line);
  }while(c!=EOF);
}

do{文}while(条件); と while(条件){文} の違い

while(条件){
  文
}
は「条件」が一回も成り立たなければ、「文」は一回も実行されない。
do{ 
  文
while(条件);
は初めに「文」が実行された後、「条件」が調べられ、成り立っていたら再度「文」を実行する。 「条件」が一回も成り立たなくても、「文」は一回は実行される。

演習10-5

  1. 上記のプログラムは、 EOF のみの行を読んでも改行を印刷してしまう。 EOF だけを読み込んだ場合、改行を印刷せずにプログラムが終了するように改良せよ。
  2. テキストファイルを読み込んで、行に行番号をつけて表示するプログラムを書け
  3. テキストファイルの一行目だけを印刷するプログラムを書け
  4. テキストファイルの最初の 3 行を印刷するプログラムを書け

坂本直志 sakamoto@c.dendai.ac.jp