今回と次回は、文字や文字列を扱うプログラムを学びます。 事情があって、いままでのプリントと違い、 「これだけ読んでわかる」ように書けていません。 授業の際に補います。 また、文体が不統一ですがこの点もご容赦を。
#include <stdio.h> main() { int c; while ((c = getchar()) != EOF) { putchar(c); } }K&R2 §1.5 の例である。 キーボードから一文字読み込んではその文字を画面に書いている。 プログラムの細部についてはあとで教科書を読むこと。 EOF の意味もそこに書いてある。
この種のプログラムもいままで通り「./a.out」 として起動するが、そのあとの操作に注意が必要である。
kenroku{iwase}1% ./a.out abcdef <- 適当に打って最後に Enter abcdef <- これはコンピュータの出力 ghi <- 適当に打って最後に Enter ghi <- これもコンピュータの出力 kenroku{iwase}2% <- 行頭で Ctrl+D を打つと終了
行頭でないところで Ctrl+D を打つと別の意味になる。 Ctrl+C だと、前に説明したように、そこで実行が中止される。 試してみよ。
プログラムそのものは「一文字読んではその文字を出力し……」 という形をしているが、 実際には改行が入力されてから一行をまとめて処理する。
あとでまた使うので、 上のプログラムをコンパイルしてできた実行ファイルの名前を a.out から mycat に変えておこう。 すると、「./a.out」ではなく「./mycat」 として起動することになる。試してみよ。
#include <stdio.h> #include <ctype.h> main() { int c; while ((c = getchar()) != EOF) { putchar(toupper(c)); } }前のプログラムをほんの少しだけ変えてみた。 toupper(c) は c が小文字だったら大文字に変換し、 それ以外だったらそのまま返す関数である。 これを使うときは #include <ctype.h> が必要である。
このプログラムは、 入力された文字が小文字だったら大文字に変換し、 そうでなければそのまま、出力する。
あとでまた使うので、上のプログラムをコンパイルしてできた実行ファイルの名前を a.out から toupper に変えておこう。 その上で、このプログラムを動かしてみること。
※ キーボードを「標準入力」、画面を「標準出力」と呼ぶことがある。
#include <stdio.h> char buf[4096+1]; main() { while(gets(buf)) { puts(buf); } }今度は、 char 型と呼ばれる、文字を格納する型の配列を用意して、 そこに gets() で一行をまとめて格納し、 puts() で出力するようにしてみた。 このプログラムは最初のプログラム例と同じように動作する。
ただし、コンパイル時に次のような警告が出る。
IPE2050{iwase}77% gcc test3.c /tmp/ccYOD4UZ.o: In function `main': /tmp/ccYOD4UZ.o(.text+0x11): the `gets' function is dangerous and should not be used. IPE2050{iwase}78%これは “関数 gets() は危険なので使用は避けるように” と言っているのである。 その通りなのだが、 初級のプログラミングでは便利なので使うことにする。
教科書 §1.9 も参照のこと。
次のプログラムでは、puts() する前に自作関数 rev() で行を反転している。 関数 rev() ではまだ習っていないことがらを使っているので、 わからない点があって構わない。
#include <stdio.h> char* rev(char*); char buf[4096+1]; int main() { while (gets(buf)) { puts(rev(buf)); } return 0; } /* 文字列を反転する。日本語 euc に対応 */ char* rev(char* buf) { unsigned char *p, *q; unsigned char tmp; /* 文字列の終わりを探しつつ2バイト文字の1バイト目と2バイト目を入れかえる */ for (q = (unsigned char *) buf; *q != '\0'; q++) { if (*q & 0x80) { tmp = *q, *q = *(q+1), q++, *q = tmp; } } /* よくあるやりかたで文字列全体を反転する */ for (p = (unsigned char *) buf, q--; p < q; p++, --q) { tmp = *p, *p = *q, *q = tmp; } return buf; }このプログラムをコンパイルして得られる実行ファイルの名前を rev に変えておこう。 それから、このプログラムで入出力リダイレクトなどを実験してみよ。
※ この linux には、 同様に動く rev というプログラムがすでにはいっているようである。
unix にたいていはいっているプログラムです。 「sort」として起動します。 複数の行を入力し、最後に Ctrl+D を押すと、 それらをアルファベット順に並べ直して出力します。 これも入出力リダイレクトが効きますから試してください。
以下の練習をやってみましょう。
※ 標準入力をを処理して標準出力に送るプログラムを 「フィルター」と呼ぶことがあります。
次のプログラムは、各入力行の長さを調べ、行頭に付して表示します。
#include <stdio.h> char buf[4096+1]; main() { int i; while(gets(buf)) { for (i=0; buf[i] != '\0'; i++) { ; } printf("%3d: ", i); puts(buf); } }
次のプログラムは、各入力行末のスペースを取り除いて出力します。
#include <stdio.h> char buf[4096+1]; main() { int i; while(gets(buf)) { for (i=0; buf[i] != '\0'; i++) { ; } for (i--; buf[i] == ' '; i--) { ; } buf[i+1] = '\0'; puts(buf); } }「cat -E filename」ファイルを画面に出力しますが、 その際、行末に「$」をつけます。 これと組み合わせて使うと効果がよくわかります。
今までの知識を利用して、 「入力の各行末に $ を付加して画面に出力するプログラム」 を自分で書くことも(きっと)できます。
これを、 gets() と puts() の組み合わせではなく getchar() と putchar() の組み合わせで書くことも、 たぶんできます。 考えてみてください。
今回と次回とで、このプリントの内容を実習し、レポートしてください。 やったことを全部書く必要はありません。 「私は(それなりに)勉強しました」 ということが私に伝われば十分です。
宛て先は私(岩瀬)の実習用アカウント cf2135@mailedu1.ipc.kanazawa-u.ac.jp です。 件名は「kadai03」(←全て半角文字、アルファベットは小文字、 途中にスペースをいれない)としてください。 自分の学籍番号、氏名(として大学に届けてあるもの) をメールの本文内に書くのを忘れずに。
締め切りは 2003 年 02 月 07 日(金)17 時。
※ 次回に、今回の実習の補足を配るかも知れません。 その場合は、その部分をレポートしても構いません。
来年度前期の計算科学科の科目「 」(後藤俊一先生担当、金曜日1限)は、 数学科の3年生にも履修を認めてくださるそうです。 (他の他学科科目が履修可能かどうかは各自で確認してください。)