Cプログラミング入門 第2回


1.5 文字入出力

 Cはもともとシステム記述用の言語なので,文字データの操作を行うことが 多いです。テキスト(文字データ)の入出力はテキストストリームとして 扱われます。人間にとっては文学的に意味がある文章も,コンピュータに とっては『スペースで区切られた文字が,改行文字でつなげられた, なが〜い文字列』としてしか認識されません。そしてファイルの最後には, EOF(End Of File)が入ります。

This is a pen. This is
a desk.
というテキストファイルは,
This[]is[]a[]pen.[][]This[] is[]\n a[]desk.[][][EOF]
となります([]はスペース)。

ちょっと余談
 更にいえば,コンピューター内部では,これらの文字は「アスキー文字 コード」で取り扱われます。例えばAは65,aは97というように(いずれも 10進表記)。だから,文字は,char とともに int でも扱うことができます。

 ここでは,入出力に二つの関数を使います。これらもstdio.hのライブラリに 入っています。

インc=getchar() 呼ばれると,自動的にcに1文字を代入する。
アウトputchar(c) 呼ばれると,cの内容を表示する。


1.5.1 ファイルの複写

< 問題 >
入力した文字をそのまま出力せよ

これをプログラムにすると,

main()
{
  変数cをintと宣言;
  c=getchar()で一つ文字をとってきてcに代入;
  while(cがEOFでないなら)終わりでないからループに入る{
    putchar(c)で文字cを表示する;
    c=getchar()で一つ文字をとってきてcに代入;
  }
}

となります。これは,作成→コンパイル→走らせる→入力待ちになるので 文字を入力→終わりはC-d(これがEOF)を入力。としてやればよいです。

またちょっと余談
 シェルのところでやったリダイレクションを利用すれば,すでに作って あるファイルを入力として,そのコピーを表示させることもできます。
a.out < filename [return]

 このプログラムは,「c=getchar()で一つ文字をとってきてcに代入」が 2箇所あるのでかっこ悪いですね。そこで次のようにしてやりましょう。

main()
{
  int c;

  while (  (c=getchar() ) != EOF){
    putchar(c);
  }

ここで,while の中の内側の括弧 ( c=getchar() ) を忘れないこと。 忘れると getchar()!= EOF が先に行われ,cには0が入るだけでになります。 どんな文字を入力しても0が出力されてしまいます。EOFで始めて1が 出力されるでしょう。おまぬけです。


1.5.2 文字数のカウント

 ここから「カウンター」という概念を導入します。「カウンター」というと 仰々しいですが,要は次のようなものです。

  1. はじめに0にリセットする。
  2. 何かあるたびに,プラス1する.
これだけのものです。

 で,ここではカウンターとして,整数の変数 nc を使いましょう。nc とは number of characters です。文字数は getchar() を呼んだ回数と 等しいですから,カウンターを getchar() を呼ぶたびにプラス1してやれば, 文字数を数えることができます。mainの中味です。

変数 ncを整数と宣言;
カウンターncを0にリセット;
while(とってきた文字がEOFでないなら)ループに入る
  カウンターncをプラス1;
結果(カウンターncの値)をプリントアウト;

ここで,++ncというのは,nc = nc + 1 と同じことであり,nc の値を 1増やしてから値を使用する(前置演算子)ということです。 似たようなものに後置演算子 nc++( nc の値を使用してから値を1増やす) があります。ここでは,前置演算子のみを使います。細かい事を いわなければ,どちらも『変数nc の値を1増やす』という意味です。

 for文で書くこともできます。

for (カウンタnc=0;とってきた文字がEOFでない;++nc)
   ;
よりスマートです。ここでは,for 文は,カウントするだけに使われますから, 実行文は不要です。これを「空文」と呼びます。何もしないということを ちゃんと表すために,インデントと行替えはやりましょう。

 whileや for文は,ループに入る前で(式)のテストが行われます。

ちょっと余談
 ループの後でテストするものにはdo....while文があります。これは 最低1回ループが実行されます。


1.5.3 行数のカウント

 行数を数えるということは,改行文字を数えるということです。'\n' と 行数は1対1対応していますから……。getchar()で文字を一つづつとってきて, '\n'が来たときだけカウンターをプラス1してやればよいのです。今度の カウンターは nl (number of lines)です。

変数 nlを整数と宣言;
カウンターnlを0にリセット;
while(とってきた文字がEOFでないなら)ループにはいる
	もし(cが'\n'と等しい)ならば
	カウンターnlをプラス1;
結果(カウンターnlの値)をプリントアウト;

これが教科書のプログラムです。ただし,改行を一度もいれずにEOFになった 場合は行数0が出力されます。これは望ましくないので,実用的にはちょっと 改良する必要があります。

ちょっと余談
 ちなみに,たとえば,文書中のeの文字の数を数えたいときには if (c == '\n') を if (c == 'e') と変えるだけでよいので,このプログラムは 文字の出現頻度を調べるのにも使えます。
 さらに文字コードも使用できます。'e'なら101だから,if (c==101)でも 同じです。


1.5.4 単語のカウント

< 問題 >
入力したテキストの文字数,行数,単語数を数えるプログラムを 書け

 これは,よくばったプログラムです。でも出来たなら,役に立ちます。 文字や行の数を数えることはすでにやりましたが,単語の数はどう数えよう。 このような問題に対した時はまず,『単語とは何か』というふだん アタリマエと考えていることから定義する必要があります。 コンピューターは,融通がききません。
 ここでは,単語を『空白,タブ,改行を含まない任意の文字列(つまり, 空白,タブ,改行で区切られた文字列ということ)』と定義します。著者は, 「単語の外」と「単語の中」というものを考えました。

 This  is a p en.  W

普通の文字なら「状態は単語の中」,スペースやタブや改行なら「状態は 単語の外」というわけです。単語数のカウンター nw (number of words) は, 『状態が「外」から「中」に入った時』だけにプラス1してやれば良いのです。 「外」から「外」,「中」から「中」のときは,無視すればよろしい。

{
全てのカウンターnc, nl, nw, を0にリセット;
まず,状態(stete)を外にしておく(OUT);
while(とってきた文字cがEOFでないなら)ループにはいる
  文字数カウンタncをプラス1;
  もし(cが'\n'と等しい)ならば
    行数カウンタnlをプラス1;
  もし(cが' 'か'\n'か'\t'のどれかと等しい)ならば
    状態を『単語の外』にして,はじめにもどる;
  そうでなくて,もし(今の状態が『単語の外』)ならば{
    状態を『単語の中』に変えて;
    文字数カウンタnwをプラス1;
        }
結果(カウンタの値)をプリントアウト
}

どこから手をつけたら良いか分からない問題でも,『単語の外』『単語の中』と いう概念を導入することによって解決することが出来ます。

ちょっと一人言
 もっと簡便なもので良ければ,『空白,タブ,改行の数を数えてそれに1を足す プログラム』でもいいのですが(不完全だけどね)。ある問題に対して,それを 解決するプログラムは一つではありません。

 前にもちょっと出てきましたが,ここで,条件判断文( if 文)が出てきました。 書式は,

if (式)
  文;
です。(式)が「真」ならば文を実行し,「偽」ならば実行しません。 if 文は, 上のプログラムのように if (,,,) ....... else if (,,,) ...... で 何段にもすることが出来ます。複雑な判断の時には段数も増えます。
 これを,’古典的な’ゲームキャラクターで説明します。レバーの向きで キャラクターが動きます。

これを if 文で書くと,
if(レバーが左)
  左にうごく;
else if(レバーが右)
  右にうごく;
else if(レバーが上)
  上にうごく;
else if(レバーが下)
  下にうごく;
else
  その場に止まる;
と,なります。

 もちろん,論理式「かつ(and;&&)」と「または(or;||)」については, ここでは説明はしません。こんなのは当たり前ですからね!


次へ進む
C入門もくじへ
小矢野のホームページへ

このページの御感想・御意見は koyano@jaist.ac.jp まで。