scanf()関数


スキャン集合

scanf()関数も、printf()関数同様に多数のフォーマット指定子が存在します
その多くはprintf()と同じなので説明しません

しかし、scanf()関数の中で特別変わったフォーマットがあります
それがスキャン集合指定子です

これまで、scanf()関数を使ってきて様々な問題にぶち当たったと思います
たとえばscanf()はホワイトスペースに遭遇すると読み込みを中断します
スペースやタブなどをscanf()関数から入力することはできませんでした

また、プログラマが予想もしない値を代入される可能性もあります
文字を入力しろといっているのに数値を入力したり、その逆だったりということもあります
ユーザーは気まぐれなので、それぞれの対応を考えなくてはなりません

そこでスキャン集合指定子で指定します
スキャン集合指定子は指定した文字だけを読み込む指定子です
これは、%のあとに大括弧 [ ] を指定し、括弧の中に文字を読み込みます
#include <stdio.h>

int main() {
        char yn;
        printf("猫耳は好きですか?y/n>");
        scanf("%[yn]" , &yn);

        printf("入力された値 = %c" , yn);
        return 0;
}
この場合、scanf()は y か n しか読み取りません(大文字のYやNは読み取りません)
文字列や数列を入力する場合、最初に指定文字以外の文字が現れた時点で読み取りを中断します

たとえば、0〜9までの値を文字列として集合で指定した場合
10A10 と入力するとAが現れた時点で入力を中止します
#include <stdio.h>

int main() {
        char str[128];
        printf("数値を入力してください>");
        scanf("%[0123456789]" , str);

        printf("入力された値 = %s\n" , str);
        return 0;
}
しかし、読み取るべきスキャン集合が非常に多い場合は面倒です
一行が長くなるため、ソースも見づらくなりますね
そのような場合は読み取らない文字を指定します
スキャン集合の一部ではないことを指定するには ^ を集合の前に指定します

^ のあとに指定された文字は、集合から外されます
以下のプログラムの場合は、読み取らない文字を指定しています
#include <stdio.h>

int main() {
        char str[128];
        printf("文字列を入力してください>");
        scanf("%[^0123456789\n]" , str);

        printf("入力された値 = %s\n" , str);
        return 0;
}
注目してほしいのは、改行文字を指定していることです
ここで改行文字を指定しなかった場合改行も読み込まれてしまうのです
もちろん、それを要求している場合は改行も集合の一部にすれば良いのです

この方法を用いれば、ホワイトスペースを読み取ることも可能です
#include <stdio.h>

int main() {
        char str[128];
        printf("文字列を入力してください>");
        scanf("%[^\n]" , str);

        printf("入力された値 = %s\n" , str);
        return 0;
}
これで空白やタブをscanf()から読み取ることができます
Kitty on your lapと入力すれば、改行までがstrに代入されます

しかし、scanf()はあくまで数値を読み込むためであり、文字列の入力に適した関数とは言えません
数値以外の型の入力に、scanf()が重宝されることはありません

余談(?)ですが、小文字だけ読み取りたいとか、大文字だけという場合
またはAからGまでの文字を入力させたいということもあるでしょう
これをすべてスキャン集合指定子で記述するのは非常に面倒な作業になります

たとえば小文字のaからzまでを指定する場合 %[a-z] と記述することができます
大文字の場合は %[A-Z] 小文字と大文字と数値ならば %[a-zA-Z0-9] と指定できます
ただし、ハイフンによる指定はANSI Cの規定外です
必ずしも、全てのコンパイラで実装されているというわけではありません(しかし、一般的です)


その他の機能

scanf()関数では、今まで制御文字列ではフォーマット指定のみを行いました
%dとか%sとかですね

しかし、ここに通常の文字列などを指定することもできます
ここでフォーマット指定子以外が指定された場合
scanf()は、制御文字列以外の文字に遭遇するまで文字を読み取り、その文字を破棄します

たとえば、一度に2つの変数へ値を入力させたい場合などに有効です
このとき、読み込みはデフォルトでホワイトベースの出現まで読み込まれます
次のプログラムで、半角スペースで区切って二つの値を入力してみてください
#include <stdio.h>

int main() {
        int i , j;
        printf("二つの数を加算します\n");
        printf("数値を空白で区切って2つ入力してください>");
        scanf("%d%d" , &i , &j);

        printf("答えは = %d\n" , i + j);
        return 0;
}
デフォルトの状態では、二つの変数への読み取りはホワイトベースで区切られます
空白やタブ、または改行コードを読み取ると、そのコードを破棄して変数に読み取ります
この場合 10 100 というように空白で分けて入力すると、10が i に 100が j に保存されます

そこで、たとえば二つのフォーマット指定子の間に文字を指定すると
この文字が一致しなくなるまで読み取り、それを破棄します
結果として、次のようなプログラムを作成することができます
#include <stdio.h>

int main() {
        int i , j;
        printf("二つの数を加算します\n");
        printf("数値を+で区切って2つ入力してください>");
        scanf("%d+%d" , &i , &j);

        printf("答えは = %d\n" , i + j);
        return 0;
}
なぜこのように、読み込むが破棄するという作業が必要なのか
それはscanf()がもつ厄介な特性のためです

scanf()関数はstdinから入力された文字列を読み取りますが
書式指定に合わなかった場合は読み取りを止めて、関数を終了します
この時、読み取られなかった文字は標準入力に残るのです

厄介なことに、この残った値は次回scanf()関数を使用したときに拾われます
これではユーザーにしてみれば、予想もしない出来事が発生することもあります

次のプログラムを実行し、最初の入力要求のときに
値をホワイトスペース(空白やタブ)で区切って、二つ入力してください (たとえば >10 20 というように)
#include <stdio.h>

int main() {
        int i , j;
        printf("iを入力してください>");
        scanf("%d" , &i);
        printf("jを入力してください>");
        scanf("%d" , &j);

        printf("i = %d : j = %d" , i , j);

        return 0;
}
iを入力してください> という入力要求時に 10 20 と入力した場合
最初の10は変数 i に%dによって読み込まれ代入されます
しかし、その後は指定されていないので、ホワイトスペースでscanf()は終了します

20は結局読み込まれなかったので、バッファに残ります
これは次の scanf("%d" , &j); が実行されたときに読み込まれてしまいます
実用プログラムを作成する場合は、このscanf()関数の特性を熟知する必要がありますね

これを理解し、フォーマット指定子以外の文字列を制御文字列内に指定することで
簡易電卓などを作成することができます。一度にオペランドと演算子を入力させることができますね
#include <stdio.h>

int main() {
	char ch;
	int i , j;
	printf("演算します。式を入力してください>");
	scanf("%d %[+-*/] %d" , &i , &ch , &j);

	switch(ch) {
	case '+':
		printf("答えは %d です" , i + j);
		break;
	case '-':
		printf("答えは %d です" , i - j);
		break;
	case '*':
		printf("答えは %d です" , i * j);
		break;
	case '/':
		printf("答えは %d です" , i / j);
		break;
	default:
		printf("入力に誤りがあります");
	}
	return 0;
}
オペランドと演算子は空白ペースで区切るものとします

printf()関数同様に、書式指定にも正確な形式があり
入力に対する制御を行うオプションを指定することができます

%[*] [最大フィールド] [{h | l |L}]フォーマット指定子

hやl , L に関しては printf() 関数と同じ意味です
shortは h longは l または L を指定することで入力することができます

まず、フォーマット指定のオプションでアスタリスク * を指定することができます
これを指定すると、指定された型は解釈されますが引数に代入されません
非フォーマット文字のように、型で代入しない型を指定することができるのです

たとえば、%d%*c%d という指定の場合で 50-50 と入力した場合
中間の文字 - は読み取られますが、引数に代入することなく破棄されます
#include <stdio.h>

int main() {
        char str[128];
        printf("数値と文字列を入力してください>");
        scanf("%*d%[^\n]" , str);

        printf("入力された値 = %s" , str);

        return 0;
}
最初に数値を入力して、続けて文字列を入力してください
scanf()は最初に数値以外の文字が出現するまで読み取り
ここで読み取った数値は代入されることなく破棄されます

また、もう一つのオプションで最大フィールド幅を指定することができます
指定方法はprintf()関数と同様です
最大フィールドは場を指定すれば、入力最大文字数を制御できますね
#include <stdio.h>

int main() {
        char str[16];
        printf("文字列を入力してください(10文字以内)>");
        scanf("%10[^\n]" , str);
        printf("入力された値 = %s" , str);

        return 0;
}
ただし、最大入力フィールドは %c に対しては意味を持ちません



前のページへ戻る次のページへ