27章 文字系の入出力処理

http://www.geocities.jp/ky_webid/c/027.html

新しい関数や概念が登場します。

今までは入力はscanf、出力はprintfのみでした。

この章ではputchar、getchar、puts、gets、fgets関数等が登場します。

ラインバッファという話が出てきました。これはとても重要な概念っぽいので抑えておきましょう。

だいたい理解できたところで問題へ。

問題1

getchar関数を使って、文字を幾つか入力させ、最後に、入力された空白の総数を表示するプログラムを作って下さい。必ず実行してラインバッファの動作を確認して下さい。

scanfはもう封印になるんでしょうかね。とりあえずgetcharを使って実装します。

int main () {
    int ch;
    int count = 0;
    
    while ( (ch = getchar()) != EOF ) {
        if ( ch == ' ' ) {
            count++;
        }
    }
    
    printf("空白は%dです。",count);
    return 0;
}
$ main
asdf adfe df
dfe dfef f ef e aaaa
^Z
空白は7です。

Ctrl+Zが押されるまで入力を受け付けてます。

ばっちりうまくいってますね。

問題2

fgets関数を使って、80文字以内の文字列を最大で10個まで入力させ、最後に、入力された順番とは逆の順番になるように、入力した文字列を表示するプログラムを作って下さい。空白の文字列が入力されたら、入力を途中で打ち切ることにします。

fgetsを使ってやってみます。

#define MAX 10

int main () {
    char str[MAX][11];
    int i = 0;
    
    for(i=0;i<MAX;i++){
        fgets(str[i],11,stdin);
        if ( strcmp(str[i]," \n") == 0 ) {
            break;
        }
    }
    
    printf("逆順で表示します。%d\n",i);
    i--;
    for(;i>=0;--i) {
        printf("%s\n",str[i]);
    }
    return 0;
}

境界値検証のため、80文字制限ではなく10文字制限としました。

だいたいうまくいくのですが、10文字以上の文字列が入力された場合、ラインバッファにデータが残っているのでその分が勝手に次の要素に格納されてしまいます。

10文字を超えた分を破棄したかったのですが、いまいちやり方がわかりませんでした。getcharでバッファが無くなるまで処理するということも考えましたが、10文字超えてない場合にもgetcharが評価されておかしなことになります。

答え合わせ

ほぼ同じような感じでした。

解答例も80文字超えた場合のラインバッファに残ってしまったデータの破棄は、してなかったのでとりあえずはそれでいいということなのかもしれません。

また最後の逆順で表示させる実装で目を見張る物がありました。

/* 入力を逆順に表示するループ */
for(--next ; next>=0; --next) {
    puts( str[next] );
}

なるほど。forの初期値設定のところでデクリメントすればいいわけですね。なんかカッコいい。

問題3

80文字以内の文字列を入力させ、それを構成する各文字を2文字に拡張して再表示するプログラムを作って下さい。例えば、"abcdef"と入力された場合、出力する文字列は"aabbccddeeff"となります。

これは問題2よりも簡単そうな匂いがします。

int main () {
    char str[81];
    int i;
    
    fgets(str,81,stdin);
    
    for(i=0;i<81;++i){
        if ( str[i] == '\0' ) {
            break;
        }
        printf("%c%c",str[i],str[i]);
    }
    return 0;
}

文字列の終端が\0だということがわかっていれば迷うことはないですね。

また上記はfor文ですが、一応while文でも実装してみました。

int main () {
    char str[81];
    int i = -1;
    
    fgets(str,81,stdin);
    
    while( str[++i] != '\0' ) {
        printf("%c%c",str[i],str[i]);
    }
    return 0;
}

ちょっと色々頑張ってみました(笑)

インクリメントしつつ評価したかったので考えた結果、iの初期値を-1に設定して実装しました。

うまく動いてます。

答え合わせ

ああ、なるほど。解答例では先にstrlenを使って文字列の長さを取得し、それをfor文の条件式に利用していますね。

確かにその方が綺麗だと思いました。

まだまだ応用が身についてないですね・・・。