第 4 回 ファイル

本日の内容


このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。

4-1. 前回の演習の復習

文字列をつなげるには


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

  /* 文字列をつなげる処理 */
  printf("文字列 %s と %s をつなげると %s になる\n",x,y,z);
}

C 言語には文字列型という変数はありません。 従って、「文字列型変数へ文字列を代入」という操作はできません。 あるのは、文字型だけなので、一文字一文字コピーしていくしかありません。 まず、問題を簡単にするために、文字配列 z[] に x[] をコピーしていくこと を考えましょう。 これは、 x[0] を z[0] に、 x[1] を z[1] にと、文字列が終るまで('\0' が見 つかるまで)繰り返せば良いので、次のように書けます。


int i;
for(i=0;x[i]!='\0';i++){
  z[i]=x[i];
}

但しこの手法だと、 '\0' を検出するとコピーを止めますので(図1)、 '\0' 自体はコピーされません。 従って、z[] の最後の文字は '\0' で終ってません。 但し、コピーが終った後、 x[i] は '\0' を示しています。そして z[i] は x[i] をコピーし終った直後を示してます。 そこで、z[i] に y[0] を、z[i+1] に y[1] をと y[] が終るまでコピーすれ ば、これで x[] と y[] をつなげることになります(図2)。 別の変数 j を用意すれば、z[i+j] に y[j] をコピーすれば良いので、次のよ うに書けます(j は宣言済みとします)。


図1

図2

for(j=0;y[j]!='\0';j++){
  z[i+j]=y[j];
}

そして、z[] の最後に '\0' を入れます。 これらをまとめると、文字列の結合ができます。


#include <stdio.h>
main(){
  char x[]="abc";
  char y[]="def";
  char z[50];
  int i,j;
  for(i=0;x[i]!='\0';i++){
    z[i]=x[i];
  }
  for(j=0;y[j]!='\0';j++){
    z[i+j]=y[j];
  }
  z[i+j]='\0';
  printf("文字列 %s と %s をつなげると %s になる\n",x,y,z);
}

なお、 z[i+j] の代わりに z[i] とし、 j と一緒に i を増やしていっても良 い。

4-2. 式の値

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){}

4-3. 標準入出力

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

標準出力

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

dir > file1

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

echo abc > file2

標準入力

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

4-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 文が必要 になります。但し、コマンドプロンプトでは大丈夫です。

4-5. 演習

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

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

  2. gcc Developer Station 2000 で作られた exe ファイルは標準入出力関係 がおかしいです。したがって、下の演習は無意味です。
    MS-DOS プロンプト、またはコマンドプロンプトで、次の指示に従いなさ い。
    1. コマンド verの出力をファイルに保存しなさい。
    2. コマンド dir /wの出力をファイルに保存しなさい。
    3. 次のプログラムの出力をファイルに保存しなさい。
      #include <stdio.h>
      main(){
        printf("Hello World!\n");
      }
      
      なお、 gcc Developer Station 2000 でコンパイルすると、プロジェクトファ イルのあるディレクトリに Debug というディレクトリが出来、その中に実行 可能な exe ファイルが作られます。

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

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

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


  5. ファイルの中の行が何行あるかを数えるプログラムを書きなさい。
  6. ファイルの中の各行がそれぞれ何文字あるかを出力するプログラムを書き なさい。
  7. 入力ファイルに対して、一行が長い場合、 10 文字ずつ折り返すプログラムを 作りなさい。


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