web-dev-qa-db-ja.com

唯一の入力であっても、scanfから改行文字を取得する方法

ループのサイズを表す整数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
}
7
user2018675

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を使用してください。それは良いです。

12
Daniel Fischer

これには2つの解決策があります。

  • fgetsの代わりにscanfを使用してください。
  • 文字列の最後に\nを追加します。これは、使用によって入力が\nで終了することがわかっているためです。

最初の解決策:

...
char buf[28];
fgets(buf, 28, stdin);
...

2番目の解決策:

#include <string.h>

...
char buf[28];
scanf("%s", buf);
strcat(buf, "\n"); // add the newline to the string
...
2
qwertz

はい、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.
}
0
jetty

1つのオプションは、getsを使用して一度に1行を読み取り、次にsscanfを使用して各行を解析することです。

コメントに基づいて編集:バッファオーバーランを回避できるため、fgetsを使用する方が適切です。

0
rasmus