今回と次回は、文字や文字列を扱うプログラムを学びます。 事情があって、いままでのプリントと違い、 「これだけ読んでわかる」ように書けていません。 授業の際に補います。 また、文体が不統一ですがこの点もご容赦を。
#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年生にも履修を認めてくださるそうです。 (他の他学科科目が履修可能かどうかは各自で確認してください。)