「C言語の基礎 - 標準入力から安全に文字列を取得する」の版間の差分
113行目: | 113行目: | ||
== fgets関数の利用 == | == fgets関数の利用 == | ||
<code>fgets</code>関数は、改行も1行に含めて一緒に取得する。<br> | |||
削除する場合は、各自でコードを記述しなければならない。また、<code>strlen</code>関数を使用するなどして、文字列長を求めることも必要である。<br> | |||
<br> | <br> | ||
また、戻り値は以下のようになる。<br> | また、戻り値は以下のようになる。<br> | ||
* 1文字以上読み取れた場合は、buffer(改行だけでも1文字以上という扱いになる) | * 1文字以上読み取れた場合は、buffer(改行だけでも1文字以上という扱いになる) | ||
* | * 最初から[Ctrl] + [D](Windowsの場合は[Ctrl] + [Z])でEOFが入力された場合は、<code>NULL</code> | ||
* | * 失敗した場合は、<code>return 1;</code>としてプログラムを異常終了扱いにさせるとよい。 | ||
<br> | <br> | ||
以下の例では、必ず1文字以上入力させて改行を削除している。<br> | 以下の例では、必ず1文字以上入力させて改行を削除している。<br> | ||
< | <syntaxhighlight lang="c"> | ||
#include <stdio.h> | #include <stdio.h> | ||
#include <string.h> | #include <string.h> | ||
147行目: | 147行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
以下の例では、改行だけの入力を認めて改行を削除している。<br> | 以下の例では、改行だけの入力を認めて改行を削除している。<br> | ||
< | <syntaxhighlight lang="c"> | ||
#include <stdio.h> | #include <stdio.h> | ||
#include <string.h> | #include <string.h> | ||
175行目: | 175行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
以下の例では、改行だけの入力を認めて改行を削除している。<br> | 以下の例では、改行だけの入力を認めて改行を削除している。<br> | ||
< | <syntaxhighlight lang="c"> | ||
#include <stdio.h> | #include <stdio.h> | ||
195行目: | 195行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br><br> | |||
== 整数の読み取り == | |||
==== scanf関数の使用 ==== | |||
整数を受け取る場合、一般的に<code>scanf</code>関数の<code>%d</code>を使用することが多い。<br> | |||
<br> | |||
以下の例では、複数個の数値が入力された時、2個目以降は無視している。<br> | |||
ただし、以下のような欠点が存在する。<br> | |||
* 最初が空白文字の場合、<code>scanf</code>関数が終了しない。 | |||
* 範囲外の値が入力された場合、対応できない。 | |||
<syntaxhighlight lang="c"> | |||
#include <stdio.h> | |||
int main() | |||
{ | |||
int num; | |||
printf("Input: "); | |||
if (scanf("%d%*[^\n]", &num) != 1) | |||
{ | |||
return 1; | |||
} | |||
scanf("%*c"); | |||
printf("Output: %d\n", buffer); | |||
return 0; | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
==== scanf関数とstrtol関数の使用 ==== | |||
<code>scanf</code>関数のみでは上記のような欠点があるため、文字列として読み取り整数に変換する方法がある。<br> | |||
<br> | |||
文字列から整数に変換するには<code>strtol</code>関数を使用するため、整数は<code>long</code>型とする。<br> | |||
なお、<code>atoi</code>関数や<code>atol</code>関数にはエラー検出機能は無いので使用しないこと。<br> | |||
<syntaxhighlight lang="c"> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <errno.h> | |||
#include <limits.h> | |||
int main() | |||
{ | |||
char buffer[32], *endptr; | |||
long num; | |||
// 文字列として読み取る | |||
printf("Input: "); | |||
if (scanf("%31[^\n]%*[^\n]", buffer) != 1) | |||
{ | |||
fprintf(stderr, "Error: No input specified\n"); | |||
return 1; | |||
} | |||
scanf("%*c"); | |||
// 文字列から整数に変換する | |||
num = strtol(buffer, &endptr, 10); | |||
if (*endptr != '\0') | |||
{ | |||
fprintf(stderr, "Error: Invalid charcter found: %c\n", *endptr); | |||
return 1; | |||
} | |||
if (errno == ERANGE) | |||
{ | |||
fprintf(stderr, "Error: Out of range (%s)\n", num == LONG_MAX ? "Overflow" : "Underflow"); | |||
return 1; | |||
} | |||
printf("Output: %ld\n", num); | |||
return 0; | |||
} | |||
</syntaxhighlight> | |||
<br><br> | |||
== 指定サイズ内で複数行を読み取る == | |||
<code>fread</code>関数を使用する場合、行という概念にとらわれず,格納領域が全て埋まる、または、入力が<code>EOF</code>に到達するまで読み取ることができる。<br> | |||
ただし、バイナリモードで読み取るため改行コードの自動変換の機能が無い。そのため、テキストを処理する場合は不便である。<br> | |||
<br> | |||
<code>scanf</code>関数では、<code>%4095[\x01-\xff]</code>のように記述することにより、<code>NULL</code>文字以外の文字を全て読み取ることができる。(改行も読み取ることができる)<br> | |||
<syntaxhighlight lang="c"> | |||
#include <stdio.h> | |||
#include <string.h> | |||
int main() | |||
{ | |||
char buffer[4096] = {0}, | |||
*p = NULL; | |||
printf("Input: \n"); | |||
if (scanf("%4095[\x01-\xff]", buffer) != 1) | |||
{ | |||
return 1; | |||
} | |||
printf("Output: \n"); | |||
for (size_t i = 0, p = strtok(buffer, "\n"); p; i++, p = strtok(NULL, "\n")) | |||
{ // 確認用にstrtok関数を使用して行ごとに番号を付加して表示する | |||
printf("[%zu] %s\n", i, p); | |||
} | |||
return 0; | |||
} | |||
</syntaxhighlight> | |||
<br><br> | <br><br> | ||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:C]] | [[カテゴリ:C]] |
2021年11月11日 (木) 19:42時点における版
指定サイズ内で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;
}
整数の読み取り
scanf関数の使用
整数を受け取る場合、一般的にscanf
関数の%d
を使用することが多い。
以下の例では、複数個の数値が入力された時、2個目以降は無視している。
ただし、以下のような欠点が存在する。
- 最初が空白文字の場合、
scanf
関数が終了しない。 - 範囲外の値が入力された場合、対応できない。
#include <stdio.h>
int main()
{
int num;
printf("Input: ");
if (scanf("%d%*[^\n]", &num) != 1)
{
return 1;
}
scanf("%*c");
printf("Output: %d\n", buffer);
return 0;
}
scanf関数とstrtol関数の使用
scanf
関数のみでは上記のような欠点があるため、文字列として読み取り整数に変換する方法がある。
文字列から整数に変換するにはstrtol
関数を使用するため、整数はlong
型とする。
なお、atoi
関数やatol
関数にはエラー検出機能は無いので使用しないこと。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
int main()
{
char buffer[32], *endptr;
long num;
// 文字列として読み取る
printf("Input: ");
if (scanf("%31[^\n]%*[^\n]", buffer) != 1)
{
fprintf(stderr, "Error: No input specified\n");
return 1;
}
scanf("%*c");
// 文字列から整数に変換する
num = strtol(buffer, &endptr, 10);
if (*endptr != '\0')
{
fprintf(stderr, "Error: Invalid charcter found: %c\n", *endptr);
return 1;
}
if (errno == ERANGE)
{
fprintf(stderr, "Error: Out of range (%s)\n", num == LONG_MAX ? "Overflow" : "Underflow");
return 1;
}
printf("Output: %ld\n", num);
return 0;
}
指定サイズ内で複数行を読み取る
fread
関数を使用する場合、行という概念にとらわれず,格納領域が全て埋まる、または、入力がEOF
に到達するまで読み取ることができる。
ただし、バイナリモードで読み取るため改行コードの自動変換の機能が無い。そのため、テキストを処理する場合は不便である。
scanf
関数では、%4095[\x01-\xff]
のように記述することにより、NULL
文字以外の文字を全て読み取ることができる。(改行も読み取ることができる)
#include <stdio.h>
#include <string.h>
int main()
{
char buffer[4096] = {0},
*p = NULL;
printf("Input: \n");
if (scanf("%4095[\x01-\xff]", buffer) != 1)
{
return 1;
}
printf("Output: \n");
for (size_t i = 0, p = strtok(buffer, "\n"); p; i++, p = strtok(NULL, "\n"))
{ // 確認用にstrtok関数を使用して行ごとに番号を付加して表示する
printf("[%zu] %s\n", i, p);
}
return 0;
}