サイトマップ / C言語講座出入り口総目次目次:入出力(2)>gets( )とscanf( )の問題点の解決

青い直線

gets( )とscanf( )の問題点の解決

青い直線

[キーボードから1行入力]←このソース→[書式付き入力と書式指定子]

/* 今日は、標準ライブラリ関数gets( )の問題点と、fgets( )による問題解決の話です。

    #include <stdio.h>
    char *gets(char *s);

    使用例:gets(s);

    実行結    戻り値
    成功        s
    失敗      NULL

gets( )は、標準入力(stdin:キーボード)から1行を読み込みsにしまいます。改行コード(\n)または EOF に出会うと、s に ヌル文字(\0)を追加して戻ります。次のプログラムを検討して見て下さい。重大な問題点があります。

   #include <stdio.h>

   void main(void);

   void main(void)
   {
      char name[21];
		
      printf("お名前を入力して下さい\t");
      gets(name);
      printf("\nあなたのお名前は %s です。\n", name);
   }

文法は間違っていません。上記のプログラムをコンパイルして実行してみても、正常に動作するように見えるかも知れません。name[ ]には末尾にヌル文字が追加されるので、英文字または英数字20字迄をしまうことができます。もし、この限度を超えて入力があると、割り当てられたメモリ範囲を超えて書き込みが行われます。ハードウエアにより何が起こるかは不定ですが、動作が異常になるか、最悪の場合システムがクラッシュして暴走します(リセットしなければならなくなる)。

これに対処するにはどうすれば良いでしょうか。一つの解決法として、最初のprintf( )文の中に入力は20字以内というフレーズを挿入すれば良いかも知れません。しかし、これには暗黙の前提があります。「キーボードをたたくのは文字を読める人間」。キーボードを幼児がおもちゃにするかも知れません。ネコがキーボードの上で昼寝するかも知れません。この方法では根本的な解決にはなりません。gets( )を使う限り入力の量を抑制する方法はありません。

fgets( )

根本的に解決するには、gets( )を使わないことです。代わりにfgets( )を使います。

    #include <stdio.h>
    char *fgets(char *s, int n, FILE *fp);

    使用例:fgets(s, n, fp);

    実行結果    戻り値
    成功       読み込んだ文字列
    失敗       NULL

fgets( )は fp から、n - 1 個以内の文字を配列 s に読み込みます。改行コード(\n)に会うと、改行コードにヌル文字を追加して戻ります。EOF に出会うとヌル文字を追加して戻ります。キーボードからの入力では、リターンキーを押下して入力を終了させます。このため、文字列の末尾のヌル文字の前に改行コードがあります。これをそのまま画面に表示すると、改行が行われてしまいます。そこで、改行コードを削ります。

strlen( )はヌル文字を含まない文字列の長さを返します。C言語の配列の添え字は0から始まります。'name[strlen(name) - 1]'は改行コードのある要素になります。そこで、下記のコードで改行コードを消します。

    name[strlen(name) - 1] = '\0';

fgets( )を使った改良版が今回のソースプログラムです。入力文字数が20字を越えると、20字目までの文字にNULL文字を追加したものが、name[21]に入ります。20字を越えた文字はどうなるでしょう。標準入力に残っています。

ソースプログラムの説明

今回のソースプログラムでは、fgets( )に続いてscanf( )でキーボードから文字列を入力しています。何の処置もしないと、残っている文字をscanf( )が取り込んで期待と異なる動作をしてしまいます。そこで、fflush( )で残っている文字を捨ててから、scanf( )を呼び出しています。fflush( )を無効にして、20字を越える入力をfgets( )に対して行って見て下さい。残っている入力したデータを、scanf( )が取り込みます。 */

/* ここからソースプログラム */

#include <stdio.h>
#include <string.h>    /* strlen(  ) で必要 */

void main(void);

void main(void)
{
    char name[21];
    char place[21];

    printf("お名前を入力して下さい\t");
    fgets(name, sizeof(name), stdin);

    name[strlen(name) - 1] = '\0';        /* 改行コードを消す */
    printf("\nあなたのお名前は %s です。\n", name);

    printf("あなたのお生まれになったのはどこですか\t");

                                          /* fgets(  ) の入力が20字を越えたら */
    fflush(stdin);                        /* 越えた分が stdin に残っているので捨てる */

    scanf("%20s", place);
    printf("\nあなたは %s でお生まれになりました。\n", place);
}

/* ここまでソースプログラム */

/* scanf( )についても事情は同じです。配列のサイズを超えた入力の抑制ができません。なお、scanf( )ではスペースも区切り文字として使うので、下記のコードに対して、"ABC DEF"と入力すると、placeには、"ABC"のみが入ります。

	scanf("%20s", place);

*/

[キーボードから1行入力]←このソース→[書式付き入力と書式指定子]

青い直線

/* (C) 2000- YFプロ. All Rights Reserved. */    提供:C言語講座−それ自体コンパイルできる教材を使った講座です−

青い直線

サイトマップ / C言語講座出入り口総目次目次:入出力(2)>gets( )とscanf( )の問題点の解決