MasaのITC Life

夢は起業家!全てにおいて凡人だけど頑張ることだけはいっちょ前!

プログラミング

【Cの標準入力】scanf/fgets/getchar/getsの違いと「改行・スペース」のまとめ

投稿日:2020年2月19日 更新日:



かつてつまずいたことがあったので、

一度まとめておこうと思います。

まずCの標準入力と言えば、

以下が思いつくかと思います。


全て"stdio.h"に定義されています。

関数 フォーマット(書式)

scanf()

int scanf(const char *format, ....)

fgets()

char *fgets(char *s, int size, FILE *stream)

getchar()

int getchar(void)

gets()

char *gets(char *s)


これらは名前が違えば、

使い方も少し違います。



ちなみにgets()の使用は

すでに非推奨になっているので、

今回はそれ以外について説明します。



標準入力関数を理解するうえで、

最もやっかいで頭を悩ますのが、

バッファ(入力ストリーム)
の存在です。



ここでいうバッファとは、

実際にユーザが打ち込んだ文字列や数値を

一時的に入れる貯蔵庫を言います。



標準入力関数をコールする際に、

格納先であるポインタ(変数のアドレス)を

一緒に指定します。


※scanf()のイメージです。

¥n(改行)は、エスケープシーケンスの1つです。
 エンターキーが押されたことを意味します。
 



上記の画像のように、

まずユーザが入力した文字列や数は

バッファに格納されます。



そして関数をコールすることで

バッファから格納先へと移します。



コールした関数によって挙動が異なります。

例えば、改行やスペースの

取り扱い方が違ってきます。



ではどのように挙動が異なるのでしょうか。

実際に関数を順番に見ていきます。



scanf()の仕組みと改行・スペース


scanf()は文字列や数値の入力を受け付けます。



なお、abcなどの文字は受け付けますが、

改行やスペースの扱いには注意です。

期待せぬ結果を招くことになります。



実際に見てみましょう。

使うコードはこちらです。


上のコードを実行すると、文字・数値

どちらを入力しても正常に表示されます。

これはご存知かと思いますので、

文字や数値だけを入力した実行結果は省きます。




ここからがややこしくなります。

例えば次のように入力してみます。

a (スペース) dog



すると上記の結果になりました。

scanfではスペースの入力受付は出来ません。



ちなみにスペースをAsciiコードの

16進数で表現すると 0x20 です。



"a スペース dog" が入力されたら

まずはバッファに送られます。



scanf() も後で紹介するfgets() も

複数の文字の入力を読み込みますが、

実際はバッファから1文字ずつ順に

読み込んでいきます。



つまりscanf関数によって、

バッファから1文字ずつ順に

バッファから格納先へと移されます。



繰り返しますが、scanf()は

1文字ずつ順番に読み込むので、

最初の a が来たときは通常通り格納します。



ところが2文字目にスペースがきたので、

scanf()は読み込めず、終了します。



scanf()はスペースや改行に遭遇すると

読み込みを終了します。



そして2回目のscanf()で、

本来は入力を受け付ける動作を

期待していたのですが、

まだバッファに文字が残っているので、

それらを勝手に読み込みに行きます。



scanfではスペースを読み込めないので、

残っていたスペースは無視されます。



なのでバッファ中にスペース以降に保存された

"dog" を読み込みます。



ここでも g の次に改行(\n)に遭遇するので、

scanf()は読み込みを終了します。



ここで終了するとバッファにまだ

改行(\n)が残ったままになるので、

次の標準入力処理には注意が必要になります。



なのでscanf()を実際に使用する際は、

少し難しい話しになりますが、

「読み飛ばし」や「代入抑止」などの

テクニックを併せて使う必要があります。



例えば次のようなものがあります。

scanf("%100[^\n]%*c", str)
最大100文字で改行まで読み込み、そして最後の改行は読み捨てる



要素を分解すると、

%[\n]と%cが基本になります。


%[\n]は改行を読み込む

^ で逆の意味になるので%[^\n]で、

改行以外という意味になります。

そこに100とあるのは、

配列があふれるのを防ぐ目的です。



%cは一文字読み込みます。

そして * は、それを読み捨てる

という意味です。


よって改行以外(改行直前まで)を読み込み、

その次の文字(=改行)を読み捨てる、

ということになるわけです。




ここで頭がパンクしそうになった場合は、

これだけ覚えておいてください。


「scanfはスペースや改行で読み込み終了!」

ということだけを注意して下さい。


fgets()と改行・スペース



scanf()の次はfgets()です。

コードは以下を用います。



fgets()は以下を引数に指定します。

格納先ポインタ
読み込みサイズ
ストリーム(標準入力するときは stdin )



scanf() と同様に a dog と入力してみます。



fgets() は scanf()と違って、

改行・スペースに遭遇しても

しっかりと指定したサイズ分読み込みます。



ちなみに出力した16進数は、

これらを意味しています。

a = 0x61
スペース = 0x20
d = 0x64
o = 0x6f
g = 0x67
改行 = 0x0a



これだけ聞いたら、それじゃ

scanf()よりもfgets()の方が便利?じゃんか

って思うかもしれません。



実はfgets()にも注意が必要な点があります。

scanf()とfgets()の実行結果を

それぞれ見比べてみて下さい。




気づきましたでしょうか。

scanf()の場合、Resultのすぐ下に

16進数で中身が出力されています。

一方でfgets()の方は、

ResultとAfterの行間に1行空いています。



本来は空かないはずですが、

fgets()で表示した "a dog" には、

改行も一緒に出力してしまっているからです。



つまりfgets()は改行も読み込む分、

中身を出力したときに改行も一緒に、

出力されてしまう羽目になり、

見た目がおかしなことになりかねません。



実はfgets()にはまだ注意点があります。

次のコードと実行結果を見て下さい。


配列の初期化に文字 a を入れています。

Asiciiコードは0x61になります。



そこに fgets() で入力を行います。

とりあえず a だけを入力します。

すると中身が次のようになります。



1バイト目の0x61は入力した a です。

2バイト目は改行を意味する0x0aです。

問題の3バイト目です。

0x00 はどこから来たのでしょうか。



実はfgets()はこのような仕様になっています。

指定した文字数に対して、

入力した文字 + 改行 を読み込んでも

まだ余裕があれば、最後に0x00を入れます。




例えば、もしfgets()をコールした時に

7バイトの読み込みを指定した場合を考えます。

ここで入力に a a a の3バイトを入力したら、

配列には a が3つと改行の計4バイト分と、

最後に0x00が格納されます。

つまり合計5バイト埋まることになります。



次にもし入力時に a a a a a a の6バイトを

入力した場合、改行は格納されずに、

7バイト目には0x00が格納されることになります。



こんな感じです。

After_2 の箇所はただfgets()をしただけです。

改行を示す 0x0a の後ろに、fgets()の仕様に従って、

0x00が格納されています。

画像に alt 属性が指定されていません。ファイル名: 3271eb8c7446dc3cd7a922def6bfec59.jpg



確かにscanf()と違って、

改行・スペースを読み込む点は

とても便利?ですが、上述したような点に

注意する必要があります。



getchar()の使い方と改行・スペース



これまでの2つの関数と違い、

getchar() は1文字の入力のみ受け付けます。

改行・スペースも読み込みます。





実行結果は以下になります。

最初は 'a' を入力し、

2回目はただエンターを押しただけす。

エンターを押しただけなので、

改行意味する '\n' = 0x0a が入ります。





実はこの getchar() は、

scanf() で読み込むことが出来なかった

改行0x0a だけを読み込むために

使われることがしばしあります。



使い方はscanf() の後に、

getchar() の一行を追加するだけなので、

とても簡単です。




以上です。

最後まで読んでいただきありがとうございました。


-プログラミング

Copyright© MasaのITC Life , 2023 All Rights Reserved Powered by STINGER.