サイトマップ / C言語講座>出入り口>総目次>目次:入出力(2)>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( )を使う限り入力の量を抑制する方法はありません。
根本的に解決するには、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言語講座−それ自体コンパイルできる教材を使った講座です−