fgets和gets的区别

5

fgets()gets()有什么区别?

我正在尝试在用户按下“回车”时中断我的循环。使用gets()效果很好,但我不想使用gets()。我尝试使用fgets()scanf(),但是结果与gets()不同。无论用户输入什么文本,fgets()都会打破循环!这是我的代码:

void enter(void)
{
  int i,

  for(i=top; i<MAX; i++)
    {
      printf(".> Enter name (ENTER to quit): ");
      gets(cat[i].name);

      if(!*cat[i].name)
         break;

      printf(".> Enter Last Name: ");
      scanf("%s",cat[i].lastname);
      printf(".> Enter Phone Number: ");
      scanf("%s",cat[i].phonenum);
      printf(".> Enter e-Mail: ");
      scanf("%s",cat[i].info.mail);
      printf(".> Enter Address: ");
      scanf("%s",cat[i].info.address);
      printf("\n");
    }
  top = i;
}

1
为什么你不想使用 gets 函数? - Cilan
我正在使用Linux,它给我抛出了警告..我不知道,使用gets函数是否安全? - pureofpure
噢,我记得它会创建缓冲区溢出。您能解释一下为什么您不想在您的问题中使用 gets 吗? - Cilan
gets函数是危险的。为什么不应该使用它? - Milind Dumbare
@Milind Bleh,我是C语言的新手。如果有人想杀了我,请原谅我。 - Cilan
显示剩余2条评论
4个回答

4
gets()fgets()之间的区别在于,fgets()会将换行符留在缓冲区中。因此,不要检查输入的第一个元素是否为0,而应该检查它是否为'\n'
fgets(cat[i].name, sizeof cat[i].name, stdin);
if (cat[i].name[0] == '\n' || cat[i].name[0] == 0) {
    // empty line or no input at all
    break;
} else {
    // remove the trailing newline
    int len = strlen(cat[i].name);
    cat[i].name[len-1] = 0;
}

好的,我刚刚尝试了一下。但是它打破了循环,似乎是从缓冲区读取了一些文本或其他东西... - pureofpure
当您使用 scanf 读取地址时,它不会消耗行末的换行符。因此,当循环重复调用 fgets() 时,它将读取该换行符。 - Barmar
好的,那我该怎么修复这个问题? - pureofpure
最好的解决方案是不使用 scanf。使用 fgets 逐行读取,然后使用 sscanf 解析每一行。 - Barmar
好的,我将尝试使用fgets逐行读取,是否有可能创建一个包含fgets函数的函数,以避免每次都进行所有这些检查? - pureofpure
你可以编写自己的函数来调用 fgets 并删除换行符。 - Barmar

3

放弃使用gets()scanf()
创建一个帮助函数来处理并验证用户输入。

// Helper function that strips off _potential_ \n
char *read1line(const char * prompt, char *dest, sizeof size) {
  fputs(prompt, stdout);
  char buf[100];
  *dest = '\0';
  if (fgets(buf, sizeof buf, stdin) == NULL) {
    return NULL;  // EOF or I/O error
  }
  // Remove potential \n
  size_t len = strlen(buf);
  if (len > 0 && buf[len-1] == '\n') {
    buf[--len] = `\0`;
  }
  // Line is empty or too long
  if (len == 0 || len >= size) return NULL;
  return memcpy(dest, buf, len+1);
}

void enter(void)
{
  int i;

  for(i=top; i<MAX; i++)
    {
      if (read1line(".> Enter name (ENTER to quit): ", 
          cat[i].name, sizeof cat[i].name) == NULL) break;
      if (read1line(".> Enter Last Name: ", 
          cat[i].lastname, sizeof cat[i].lastname) == NULL) break;
      if (read1line(".> Enter Phone Number: ", 
          cat[i].phonenum, sizeof cat[i].phonenum) == NULL) break;
      if (read1line(".> Enter e-Mail: ", 
          cat[i].info.mail, sizeof cat[i].info.mail) == NULL) break;
      if (read1line(".> Enter Address: ", 
          cat[i].info.address, sizeof cat[i].info.address) == NULL) break;
    }
  top = i;
}

fgets()gets()的一些属性:

fgets()读取输入并保存到缓冲区直到:
1)缓冲区仅剩一个位置 - 或 -
2)遇到'\n' - 或 -
3)流达到文件结尾条件 - 或 -
4)发生输入错误。

gets()执行上述#2-#4,但扫描时不保存'\n'
gets()在C99中已被弃用,不再是C11的一部分。


哇!太棒了,我用这个完成了我的工作,除了函数参数 sizeof size,我将其更改为 int size,因为我遇到了错误!谢谢。 - pureofpure

0

你可以使用 fgets() 代替 STDIN。

这个函数是安全的,并且总是在字符串末尾插入一个 '\0'。

例如:

char inputbuffer[10];
char *p;
p = fgets(inputbuffer, sizeof(inputbuffer), stdin);
printf(">%s<\n", p);    /* p is NULL on error, but printf is fair */

在这个例子中,你最多会得到9个字符+ '\0'。


0

gets和fgets之间的问题差异在于gets会从输入行中删除尾随的'\n',而fgets则保留它。

这意味着fgets返回的“空”行实际上是字符串“\n”。

最好避免使用gets的恶劣差异是,如果您给gets一个太长的行,您的程序将以非常糟糕的方式崩溃。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接