7

我正在做作业,要求我读取一个n表示循环大小的整数,然后读取一行字符n次数并在用户输入后立即打印它。所以我使用scanf然后我用printf. 问题是如果用户的输入只是一个换行符,它应该打印另一个\n,但是scanf当它是一个时似乎忽略了输入\n

有什么方法可以完成这项任务,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
}
4

4 回答 4

11

使用fgetsto read in a line 更简单,更健壮:

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("%s",buff)因为大多数scanf转换都会忽略前导空格:

输入空白字符(由isspace函数指定)将被跳过,除非规范包含[cn说明符。

因此,如果用户输入一个空行,则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,真的。它更好。

于 2013-01-28T16:10:18.623 回答
2

我有两个解决方案:

  • 使用fgets而不是scanf.
  • \n在字符串末尾附加 a ,因为您知道使用将以 . 结束输入\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
...
于 2013-01-28T16:16:53.273 回答
0

一种选择是使用一次读取一行,gets然后使用解析每一行sscanf

根据评论编辑:使用fgets更合适,因为您可以避免缓冲区溢出。

于 2013-01-28T16:00:35.470 回答
0

是的,如果您使用正确的格式说明符,scanf()可以
做到这一点: -- 接受不超过 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() 调用之间清除标准输入:

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.
}
于 2018-11-05T06:42:40.543 回答