scanf()

scanf() の一般的な形式は次のとおり。

scanf(制御文字列, ...);

制御文字列は printf() とよく似たフォーマット指定子とその他の文字からなる。「…」の部分は可変長の引数で、制御文字列内のフォーマット指定子と対応する。これらの引数はフォーマット指定子の指定する型のアドレスでなければならない。scanf() 自体は入力されたフィールドの数を返す。

フォーマット指定子

%c文字
%d10進数
%i10進数
%e浮動小数点数
%f浮動小数点数
%g浮動小数点数
%o8進数
%s文字列
%x16進数
%pポインタ
%nこれまで読み込まれた文字数に等しい整数値を受け取る
%u符号なし整数
%[]文字集合

%c と %n 以外については最大フィールド幅を指定できる。
注意しなくちゃいけないのは %s は文字列を読み込むけど、空白文字が現れるとそこで読み込みをやめてしまうこと。例えば、

This is a test.

という文字列を %s で読み込もうとしても、「This」しか読み込まれない。

#include

int main(void)
{
    char str[80];

    printf("Input string: ");
    scanf("%s", str);
    printf("%s\n", str);

    return 0;
}
takatoh@nightschool $ ./sample_8_6
Input string: This is a test.
This

こういう場合は代わりに gets() を使う。

文字集合(スキャン集合)

%[ ] は大カッコで囲まれた文字を入力する。これをスキャン集合と呼ぶ。例えば

%[ABC]

という指定は A、B、C の続く限り読み込まれる。ほかの文字が現れた時点で次のフォーマット指定子に移る。[ ] 内の最初に ^ をつけることで否定を意味する。

#include

int main(void)
{
    char str[80];

    printf("Input string: ");
    scanf("%[a-z]", str);
    printf("%s\n", str);

    return 0;
}
takatoh@nightschool $ gcc sample_8_6b.c -o sample_8_6
takatoh@nightschool $ ./sample_8_6
Input string: ABCdefGHI
�l,

ありゃ、文字化けする。なんかおかしいんだろうか。

フォーマット指定子内の*

フォーマット指定子に * をつけると、そのフィールドは読み込まれずに捨てられる。入力に不要な文字が含まれている場合に便利。

int first, second;
scanf("%d%*c%d", &first, &second);

に対して

123-456

という入力があったとすると、first には 123、second には 456 が入力される。捨てられるフィールドに対応する引数は不要。

#include

int main(void)
{
    int first, second;

    printf("Input: ");
    scanf("%d%*c%d", &first, &second);
    printf("%d %d\n", first, second);

    return 0;
}
takatoh@nightschool $ ./sample_8_6c
Input: 123-456
123 456

制御文字列内のその他の文字の働き

制御文字列内に空白文字がある場合、scanf() は空白文字でない文字が現れるまで空白文字を捨てていく。その他の文字がある場合、一致しない文字が現れるまでその文字すべてを捨てていく。

include

int main(void)
{
    int first, second;

    printf("Input: ");
    scanf("A%d% %d", &first, &second);
    printf("%d %d\n", first, second);

    return 0;
}
takatoh@nightschool $ gcc sample_8_6d.c -o sample_8_6d
sample_8_6d.c: In function ‘main’:
sample_8_6d.c:9:5: warning: unknown conversion type character 0x20 in format [-Wformat=]
     scanf("A%d% %d", &first, &second);
     ^
takatoh@nightschool $ ./sample_8_6d
Input: AAA123    456
0 0

あれ、これもうまくいかない。

printf()

printf() はこれまでにも使ってきたけど改めて。一般的な形式は次のとおり。

printf(制御文字列, ...);

「…」の部分は可変長の引数で、制御文字列内に現れるフォーマット指定子に(数、順番ともに)対応する。printf() 自体は出力した文字数を返す。
フォーマット指定子は次のものがある。

%c文字
%d符号付き10進整数
%i符号付き10進整数
%e指数部付き表記(小文字)
%E指数部付き表記(大文字)
%f10進の浮動小数点数
%g%eと%fのいずれか短いほう
%G%Eと%fのいずれか短いほう
%o符号なし8進数
%s文字列
%u符号なし10進数
%x符号なし16進数(小文字)
%X符号なし16進数(大文字)
%pポインタを表示
%n対応する引数は整数へのポインタでなければならず、その領域にこれまで出力された文字数を書き込む
%%%記号

%%、%c、%p、%n以外は最小フィールド幅指定子と精度指定子を指定できる。また、デフォルトでは数値は右詰めで表示されるけど、-記号をつけることで左詰めにできる。ま、だいたいわかるな。

ちょっとわかりづらいのは %n だ。これは出力するためのものではなくて、出力した文字数を保存するためのものだ。次のプログラムはその例。

#include

int main(void)
{
    int i;

    printf("%d %f\n%n", 100, 123.45, &i);
    printf("Output %d characters\n", i);

    return 0;
}
takatoh@nightschool $ ./sample_8_5
100 123.450000
Output 15 characters

%n が現れるまでに 15 文字出力したと(15 文字目は改行文字)。

gets()とputs()

gets() はキーボードから文字列を読み込む。puts() はコンソールに文字列を出力する。どちらも stdio.h が必要。

gets()

キーボードからの入力というのは、ユーザーが文字列に続いて Enter キーを押すまで。Enter キーが押されると、文字列を読み込んで最後にある改行をヌル文字に置き換えたうえで引数の配列に格納する。関数自体の戻り値は、入力が成功すると配列の先頭へのポインタを、失敗するとヌルポインタを返す。
次のプログラムでは、gets() の戻り値を p に格納して、p がヌルでないことを確認してから出力している。p も str も配列(文字列)の先頭のアドレスを保持しているので、同じ文字列が2回出力されるはず。

#include

int main(void)
{
    char *p, str[80];

    printf("Input string: ");
    p = gets(str);
    if (p) { /* if p is not null */
        printf("%s %s\n", p, str);
    }

    return 0;
}

前にも書いたと思うけど、コンパイルすると gets は危険だから使うべきじゃないと警告される。

実行例:

takatoh@nightschool $ ./sample_8_4a
Input string: hello
hello hello

puts()

puts() はポインタの指している文字列を出力する。その際、自動的に改行を追加する。

#include

int main(void)
{
    puts("A");
    puts("B");
    puts("C");

    return 0;
}
takatoh@nightschool $ ./sample_8_4b
A
B
C

ちゃんと1つずつ改行されてる。
puts() は printf() よりもサイズが小さくて速い。なので、複雑な文字列を出力するのでなければ puts() を使ったほうが有利。