このページの解説は C99 をベースとしています。
以下は目次です。
第4章で、fgets関数を使って文字列の入力を受け取る方法を解説しました。今度は、整数の入力を受け取ってみます。
fgets関数は文字列の入力にしか対応していませんが、sscanf関数という関数を組み合わせることによって、整数の入力に対応できます。
実際にやってみます。次のサンプルプログラムは、整数を入力させて、それを2倍した数を出力します。
#include <stdio.h>
int main(void)
{
// まず文字列の入力として受け取る
("Please enter the integer.");
putschar input_string[20];
(input_string, sizeof(input_string), stdin);
fgets
// 整数を取り出す
int num;
(input_string, "%d", &num);
sscanf
// 2倍して出力
("%d\n", num * 2);
printf}
実行結果:
Please enter the integer.
10 <-- 入力された内容
20
Visual Studio は、このプログラムに対して警告あるいはエラーを出します。警告であれば無視して構いませんが、エラーであれば対処が必要です。この警告・エラーについては、「Visual Studio編>C4996警告」のページで解説しています。
考え方としては、整数が欲しいのだけど、まずは fgets関数を使って、文字列として入力を受け取るということです。そのあと、sscanf関数を使って、受け取った文字列から整数を取り出します。
sscanf関数は次のかたちで呼び出します。
(対象の文字列, 変換指定子を含んだ文字列, 受け取り用の変数); sscanf
このサンプルプログラムでは、次のように書いています。
(input_string, "%d", &num); sscanf
「対象の文字列」に変数input_string を指定しています。これは fgets関数によって受け取れた文字列が入っています。
「変換指定子を含んだ文字列」は、printf関数のときとよく似ています。欲しいと思っている値が整数なので “%d” 変換指定子を指定します。
「受け取り用の変数」は、「対象の文字列」から取り出された値を受け取る変数を指定します。このとき、変数名の頭に &
を付けています。変数名の頭に &
が付いていると、その変数のメモリ上での場所の住所(メモリアドレス (memory
address))を指定したことになります。単に num
とだけ書くと、変数num
が記憶している値を使おうとしていることになりますが、&num
だと場所を示していることになります。sscanf関数に「値をここへ入れてほしい」と伝えるためには、num
でなく &num
でなければなりません。
1度に2つ以上の整数の入力を受け取りたいという場合は、sscanf関数のところで対応できます。printf関数のときと同じで、変換指定子を複数書き並べ、受け取り用の変数もその個数分だけ指定します。
#include <stdio.h>
int main(void)
{
("Please enter the three integers.");
putschar input_string[60];
(input_string, sizeof(input_string), stdin);
fgets
// 整数を取り出す
int num1;
int num2;
int num3;
(input_string, "%d%d%d", &num1, &num2, &num3);
sscanf
("%d %d %d\n", num1, num2, num3);
printf}
実行結果:
Please enter the integer.
10 20 30 <-- 入力された内容
10 20 30
入力は、単純に出力の反対、という程度に思われるかもしれません。しかし、割と単純で簡単にできる出力とちがって、入力はそう簡単ではありません。入力の処理には特有の難しさがあります。
まず、入力されるデータが想定した種類のものであるという保証がありません。このページで取り上げたプログラムでは、整数が入力されることを期待していますが、「abcde」のように整数ではないものが入力される可能性があります。
入力されるデータの個数も、想定どおりに与えられる保証がありません。2つの整数を入力して欲しかったのに、1つしか入力されなかったり、3つ以上入力されたりするかもしれません。
入力されるデータの範囲も、想定どおりである保証もありません。扱いきれないほど巨大な数であったり(100000000000000000000000000000000000000)、正の数であってほしいのに負数が入力されたりするかもしれません。
このように、入力の処理では、「想定外のデータが入力される」可能性を考えなければなりません。想定していなかったからといって、プログラムが異常な動作を取ってはならないのです。
しかし、こういった問題に完璧に対処することは、少なくともここまでの知識では無理です。今のところは、自分で作ったプログラムを自分で動かすだけなので、いつも想定どおりの入力が与えられるという前提で進めていくことにします。
次のように fgets関数を呼び出したとします。
char input_string[10];
(input_string, sizeof(input_string), stdin); fgets
これは、最大で 10文字までの入力が受け取れることを意味しますが、それを超えた入力を禁じているわけではありません。実際にやってみましょう。
#include <stdio.h>
int main(void)
{
("Please enter the three integers.");
putschar input_string[10];
(input_string, sizeof(input_string), stdin);
fgets("%s", input_string);
printf}
実行結果:
Please enter the three integers.
123456789012345 <-- 入力された文字列
123456789
全部で 15文字入力してみましたが、出力されたのは 9文字です。
まず、10文字でないことに疑問があると思いますが、これは文字列の末尾にみえない文字が隠されているためです。この見えない文字は、文字列の終わりがどこなのかを知るために置かれる特別な文字で、ヌル文字(ナル文字) (null character) と呼ばれます。
入力された “123456789012345” という文字列の残りの部分、“012345” は消えてしまったわけではなく、まだ存在しています。詳しい技術解説をする段階ではないので、詳細は割愛しますが、もう1度 fgets関数を呼び出してみると、残された部分を取得できます。
#include <stdio.h>
int main(void)
{
("Please enter the three integers.");
putschar input_string[10];
(input_string, sizeof(input_string), stdin);
fgets("%s", input_string);
printf
(input_string, sizeof(input_string), stdin);
fgets("%s", input_string);
printf}
実行結果:
Please enter the three integers.
123456789012345 <-- 入力された文字列
123456789012345
実際に試すと分かりますが、2回目の fgets関数のときには、ユーザーの入力を待機することすらありません。これは、すでに1回目の fgets関数で入力された文字列がまだ残っていることが認識されているからです。新たな入力を待たなくても、まだ処理すべきデータが残っているので、待機しません。
実行結果をみると、まったく問題のない出力になったようではありますが、1回目の fgets関数で捨てられる部分があることをプログラマーが知っているから2回目の fgets関数を書いて対応できたのであって、実験だからできたことです。ユーザーがどれだけ長い文字列を入力するかわからない以上、いつもこの手が取れるわけではありません。
正確で確実な対処は難しいので、ここでは触れませんが、取り残されている入力情報というのは、実際のプログラムではかなり厄介な問題です。とりあえずは、要求以上の長さの文字列を入力しないように注意しておくことにしましょう。
【上級】もう少し詳しい話を、第43章で取り上げます。
問題① 2つの整数を受け取り、両方を足し合わせた結果を出力するプログラムを作ってください。
問題② 入力された整数を3乗した結果を出力するプログラムを作ってください。
問題③ 標準入力から秒数を受け取り、それを時・分・秒に変換して出力するプログラムを書いてください。
問題④ “x y z” という文字列には、何文字含まれていますか?(x と y の間と、y と z の間に半角の空白文字があります)
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
RSS | 管理者情報 | プライバシーポリシー |