C言語の基礎 - 標準入力から安全に文字列を取得する
指定サイズ内で1行を受け取る
次のように、文字列の格納領域が256[byte]用意されているとする。
char buffer[256] = {'\0'};
scanf関数とfgets関数の主な違いを纏めると以下のようになる。
NULL文字\0が終端に自動付与されることを考慮すると、ユーザが実質入力できるのは255[byte]までであることに注意する。
また、scanf関数はscanf("%s", buffer);のようにサイズを指定せずに使用できるが、バッファオーバーランの危険性があるので避けること。
説明 | scanf関数 | fgets関数 |
---|---|---|
基本的な使用法 | scanf("%255s", buffer) | fgets(buffer, 256, stdin) |
改行の扱い | 改行文字の直前まで読み込む | 改行文字も一緒に読み込む |
成功時の戻り値 | 読み込んだパラメータ数 | buffer |
失敗時の戻り値 | 0 または EOF (-1) |
NULL |
入力文字の取得 | できる | できない |
scanf関数の利用
scanf関数は多機能であり、フォーマットを記述することで動作のカスタマイズが可能である。
以下に、scanf
関数のフォーマットを記載する。
- %sは全ての空白文字(半角スペースも含む)を無視するため、%sの代わりに%[^\n]と指定することで、無視する対象を改行だけに限定できる。
例えば、%255[^\n]
は、改行以外の文字列を1〜255文字読み込むことを意味する。 %
の代わりに%*
を使用すると、その部分を読み飛ばすことができる。
例えば、256文字以上入力する場合、最初の255文字までを受け取り、残りを捨てる場合は、%255[^\n]%*[^\n]
と記述する。- 再度
scanf
関数を使用する場合に備えて、残った改行は%*c
で読み飛ばす。
これにより、保持されていた入力データは全て処理されたことになる。
ただし、次に読み取るものが%d
等の数値である場合には、これをしなくても特に影響はない。
また、scanf
関数は正常に読み取って変数に格納できたパラメータの数を返すので、これを成功したかどうかの判定に用いることができる。
%255[^\n]%*[^\n]
を実行した場合の戻り値は、以下のようになる。
- 1文字以上読み取れたときは 1
- 改行だけが入力されたときは 0
- 最初から [Ctrl] + [D](Linux)または[Ctrl] + [Z](Windows)でEOFが入力された時は、EOF(EOF定数の値 : -1)
- 失敗した場合は、
return 1;
としてプログラムを異常終了扱いにするとよい。
以下の例では、必ず1文字以上入力させている。
#include <stdio.h>
int main()
{
char buffer[256] = {'\0'};
printf("Input: ");
if (scanf("%255[^\n]%*[^\n]", buffer) != 1)
{
return 1;
}
scanf("%*c");
printf("Output: %s\n", buffer);
return 0;
}
以下の例では、改行だけの入力を認めている。
#include <stdio.h>
int main()
{
char buffer[256] = {'\0'};
printf("Input: ");
if (scanf("%255[^\n]%*[^\n]", buffer) == EOF)
{
return 1;
}
scanf("%*c");
printf("Output: %s\n", buffer);
return 0;
}
以下の例では、入力された文字数も取得している。(%255[^\n]の後ろは、unsigned / intの場合は%n、size_t / ssize_tの場合は%znを指定する)
(%n系は符号付き整数で取得するように定義されているが、負の値が出現する可能性がゼロなので、符号無し整数でも問題ない)
#include <stdio.h>
int main(void)
{
char buffer[256] = {'\0'};
size_t length;
printf("Input: ");
if (scanf("%255[^\n]%zn%*[^\n]", buffer, &length) != 1)
{
return 1;
}
scanf("%*c");
printf("Output: %s\n", buffer);
printf("Length: %zu\n", length);
return 0;
}
fgets関数の利用
fgets関数は、改行も1行に含めて一緒に取得する。
削除する場合は、各自でコードを記述しなければならない。
strlen関数を使用するなどして、文字列長を求めることも必要である。
また、戻り値は以下のようになる。
- 1文字以上読み取れた場合は、buffer(改行だけでも1文字以上という扱いになる)
- 最初からCtrl+D(Windowsの場合はCtrl+Z)でEOFが入力された場合は、NULL
- 失敗した場合は、そのままreturn 1;としてプログラムを異常終了扱いにさせるとよい。
以下の例では、必ず1文字以上入力させて改行を削除している。
#include <stdio.h>
#include <string.h>
int main(void)
{
char buffer[256] = {'\0'};
size_t length;
printf("Input: ");
if (fgets(buffer, 256, stdin) == NULL || buffer[0] == '\n')
{
return 1;
}
length = strlen(buffer);
if (buffer[length - 1] == '\n')
{
buffer[--length] = '\0';
}
printf("Output: %s\n", buffer);
printf("Length: %zu\n", length);
return 0;
}
以下の例では、改行だけの入力を認めて改行を削除している。
#include <stdio.h>
#include <string.h>
int main(void)
{
char buffer[256];
size_t length;
printf("Input: ");
if (fgets(buffer, 256, stdin) == NULL)
{
return 1;
}
length = strlen(buffer);
if (length > 0 && buffer[length - 1] == '\n')
{
buffer[--length] = '\0';
}
printf("Output: %s\n", buffer);
printf("Length: %zu\n", length);
return 0;
}
以下の例では、改行だけの入力を認めて改行を削除している。
#include <stdio.h>
int main(void)
{
char buffer[256];
printf("Input: ");
if (fgets(buffer, 256, stdin) == NULL)
{
return 1;
}
printf("Output: %s\n", buffer);
return 0;
}