ループのサイズを表す整数n
を読み取ってから、文字の行をn
回読み取り、ユーザーの入力の直後に出力するように求める宿題をしています。そこで、scanf
を使用してから、printf
で出力しました。問題は、ユーザーの入力が改行文字のみの場合、別の\n
を出力する必要があるが、単一の\n
の場合、scanf
は入力を無視するように見えることです。
scanf
を使用してこの割り当てを行う方法はありますか、それとも他の方法を試す必要がありますか?
int i;
scanf("%d", &i);
for(int ct=0; ct<i; ct++)
{
char buff[28];
scanf("%s", buff); // if I just press enter here
prtinf("%s\n", buff); // then I must get \n\n here
}
fgets
を使用して行を読み取る方が簡単で、より堅牢です。
_if (!fgets(buff, 28, stdin))
{
// reading failed, do appropriate error handling
// we're just exiting here
exit(EXIT_FAILURE);
}
// We have successfully read in a line, or at least the first 27
// characters of the line. Check whether a full line was read,
// if it was, whether the line was empty
size_t l = strlen(buff); // <string.h> must be included
if (buff[l-1] == '\n')
{
// a full line was read, remove trailing newline unless
// the line was empty
if (l > 1)
{
buff[l-1] = 0;
}
}
else
{
// the input was too long, what now?
// leave the remaining input for the next iteration or
// empty the input buffer?
}
printf("%s\n",buff);
_
ほとんどのscanf
変換は先頭の空白を無視するため、scanf("%s",buff)
では機能しません。
仕様に_
[
_、isspace
、またはc
指定子が含まれていない限り、入力空白文字(n
関数で指定)はスキップされます。
したがって、ユーザーが空の行を入力した場合、その形式が例外的なものでない限り、scanf
はその入力を無視します。
代わりに、scanf
を文字セット形式で使用できます。
_scanf("%27[^\n]%*c", buff);
_
改行まですべての文字を読み取り(ただし、バッファオーバーランを回避するためにここでは_28 - 1
_に制限されます)、その後、改行を格納せずに消費します(_*
_変換指定子の_%*c
_は割り当てを抑制します) 、それは完全に空白で構成される空でない行を処理しますが、_%s
_変換は処理しません。ただし、入力の最初の文字が改行の場合、_%27[^\n]
_変換は失敗し(注意を引くために chux に感謝します)、改行は入力バッファーに残され、その後のスキャンその形式では、改行が入力バッファーから削除されていない場合も失敗します。
scanf
を使用したやや堅牢な(しかし醜い;そして長すぎる入力を処理しない)ループは、私が見る限り、スキャンする前に改行をチェックする必要があります。
_for(int ct = 0; ct < i; ++ct)
{
int ch = getchar();
if (ch == EOF)
{
// something bad happened; we quit
exit(EXIT_FAILURE);
}
if (ch == '\n')
{
// we had an empty line
printf("\n\n");
}
else
{
// The first character was not a newline, scanning
// with the character set format would have succeeded.
// But we don't know what comes next, so we put the
// character back first.
// Although one character of pushback is guaranteed,
if (ungetc(ch,stdin) == EOF)
{
// pushback failed
exit(EXIT_FAILURE);
}
scanf("%27[^\n]%*c",buff);
printf("%s\n",buff);
}
}
_
本当にfgets
を使用してください。それは良いです。
これには2つの解決策があります。
fgets
の代わりにscanf
を使用してください。\n
を追加します。これは、使用によって入力が\n
で終了することがわかっているためです。...
char buf[28];
fgets(buf, 28, stdin);
...
#include <string.h>
...
char buf[28];
scanf("%s", buf);
strcat(buf, "\n"); // add the newline to the string
...
はい、scanf()can正しいフォーマット指定子を使用すればそれを実行できます:
-27文字以下の文字列を受け入れ、
-改行 '\ n'を除く、その文字列(スペースを含む)のすべての文字を受け入れるため、および
-scanf()が変換を行ったかどうかを確認するには、次のように記述します。
scanRet = scanf("%27[^\n]s", buf); // fills buf[28] with a string.
if(scanRet < 1) printf("Sorry, scanf didn't change buf[] array.\n");
注意!繰り返されるscanf()呼び出しまたは変換は、ユーザーからの予期しない入力やstdinの残りの「ジャンク」によって台無しになることがよくあります。次のように、各scanf()呼び出しの間にstdinをクリアできます。
char junk;
do {
junk = getc(stdin);
printf("junk: %c (int %d)\n", junk, (int)junk); // show me the junk we cleared
}
while(junk != '\n' || junk != EOF);
Scanf()のみを使用した私の最良の「クリーンな」ソリューションは次のとおりです。
char buf[28] = ""; // sets buf[0] = '\0';
char junk;
int i, iMax, scanRet;
printf("how many lines? (integer>0):\n");
scanRet = scanf(" %d", &iMax);
if(scanRet < 1) {
printf("Sorry, I couldn't read your integer. Bye.\n");
return 1; // error exit.
}
printf("please enter %d lines of text (<=27 chars/line):\n",iMax);
for(i=0; i<iMax; i++) {
// but first, clear any leftover junk in stdin;
do {
junk = getc(stdin);
printf("junk: %c (int %d)\n", junk, (int)junk); // VERBOSE.
}
while (junk != '\n' && junk != EOF);
// THEN try to read user's latest text string:
scanRet = scanf("%27[^\n]",buf); // [^\n]== accept ALL chars except newline
if(scanRet < 1) { // format conversion failed. Nothing new in buf.
printf("(empty line)\n");
}
else {
printf("%s\n", buf); // non-empty user string
}
}
return 0; // normal exit.
}
1つのオプションは、gets
を使用して一度に1行を読み取り、次にsscanf
を使用して各行を解析することです。
コメントに基づいて編集:バッファオーバーランを回避できるため、fgets
を使用する方が適切です。