【问题标题】:scanf("%[^\n]s",a) vs gets(a)scanf("%[^\n]s",a) vs 获取(a)
【发布时间】:2011-12-31 22:35:43
【问题描述】:

有人告诉我,用户输入字符串时不应使用 scanf。相反,大多数专家以及 StackOverflow 上的用户都使用 gets()。我从来没有在 StackOverflow 上问过它为什么不应该使用 scanf 来获取字符串。这不是实际问题,但非常感谢您回答这个问题。

现在进入实际问题。我遇到了这种类型的代码-

scanf("%[^\n]s",a); 

这会读取一个字符串,直到用户输入一个换行符,同时将空格视为字符串。

如果我使用有什么问题

scanf("%[^\n]s",a);

而不是获取?

Is 比 scanf 函数听起来更优化,gets 纯粹专用于处理字符串。请让我知道这件事。

更新

This 链接帮助我更好地理解它。

【问题讨论】:

  • gets 也不是一个好主意,因为存在缓冲区溢出的风险。使用fgets
  • @etiennedeMartel 谢谢,但据我所知 fgets 是用于文件处理的,对吗?我刚刚了解到 f 表示文件模式,所以他们为什么要使用 get 和 fgets()
  • 您可以通过将stdin 作为最后一个参数从标准输入中读取fgets。使用fgets 优于gets 的优势在于,使用fgets,您可以指定缓冲区的长度,防止fgets 读取太多 数据。 gets 存在安全风险,出于向后兼容性的原因,他们从未修复它。
  • @niko - 任何f* 函数(比如fscanf)都可以在stdin 文件句柄上使用,以模拟非f* 版本(在这种情况下,fscanf(stdin, ...) 是完全等同于scanf(...))。
  • @niko - 不,从stdin 读取将始终等待换行符。但是,如果用户输入的字符多于fgets 所要求的字符,它们将作为FILE * 结构的一部分存储在缓冲区中,并且不会被您访问。第二次调用fgets 将不再读取更多用户输入,而是从该缓冲区返回更多数据,直到它为空。 (fgetc 其他一次一个字符的函数做同样的事情。)

标签: c scanf gets


【解决方案1】:

gets(3) 很危险,应该不惜一切代价避免。我无法想象gets(3) 的用途不是安全漏洞。

scanf(3)%s 也很危险——您必须使用“字段宽度”说明符来指示您分配的缓冲区的大小。没有字段宽度,这个套路和gets(3)一样危险:

char name[64];
scanf("%63s", name);

GNU C 库为您分配缓冲区的%s 提供了a 修饰符。这个不可移植的扩展可能不太难正确使用:

   The GNU C library supports a nonstandard extension that
   causes the library to dynamically allocate a string of
   sufficient size for input strings for the %s and %a[range]
   conversion specifiers.  To make use of this feature, specify
   a as a length modifier (thus %as or %a[range]).  The caller
   must free(3) the returned string, as in the following
   example:

       char *p;
       int n;

       errno = 0;
       n = scanf("%a[a-z]", &p);
       if (n == 1) {
           printf("read: %s\n", p);
           free(p);
       } else if (errno != 0) {
           perror("scanf");
       } else {
           fprintf(stderr, "No matching characters\n"):
       }

   As shown in the above example, it is only necessary to call
   free(3) if the scanf() call successfully read a string.

【讨论】:

  • 您可以将长度修饰符与%[%s 一起使用。 %64[a-z] 最多可读取 64 个小写字母字符,%64s 最多可读取 64 个非空白字符。
  • @Adam:这会教我在空荡荡的(因此分心:)肚子上写作——我读了scanf(3)scanf(3posix)寻找一些表示我错了....但它是:输入字符串停在空白处或最大字段宽度,以先发生者为准。非常感谢!
  • char name[64]; scanf("%64s", name); 不应该是scanf("%63s", name); 吗?为NUL 字符留下一个位置。
  • @NanXiao,哇。我不敢相信我犯了那个错误。很好的收获!
  • 但是这个答案与这个问题有什么关系呢?问题是关于实现基于行的输入(而不是跳过空格)。 %sa 修饰符都没有接近主题。
【解决方案2】:

首先,不清楚s 在您的格式字符串中做了什么。 %[^\n] 部分是一个自给自足的格式说明符。正如您似乎相信的那样,它不是%s 格式的修饰符。这意味着"%[^\n]s" 格式字符串将被scanf 解释为两个独立的格式说明符:%[^\n],后跟一个单独的s。这将指示scanf 读取所有内容,直到遇到\n(留下\n 未读),然后要求下一个输入字符是s。这没有任何意义。没有任何输入会匹配这种自相矛盾的格式。

其次,显然是指scanf("%[^\n]", a)。这有点接近[不再可用]gets(或fgets),但不一样。 scanf 要求每个格式说明符至少匹配一个输入字符。如果scanf 无法匹配请求的格式说明符的任何输入字符,它将失败并中止。这意味着 scanf("%[^\n]",a) 无法读取空输入行,即立即包含 \n 字符的行。如果将这样一行输入到上面的scanf 中,它将返回0 以指示失败并保持a 不变。这与典型的基于行的输入函数的工作方式非常不同。

(这是一个相当令人惊讶且看似不合逻辑的 %[] 格式。就个人而言,我更喜欢 %[] 能够匹配空序列并生成空字符串,但这不是标准 scanf 的工作方式。 )

如果您想逐行读取输入,fgets 是您的最佳选择。

【讨论】:

  • 这篇文章的更新很好。建议将您的替代方案添加到这 2 个弱函数调用中。 (注意:scanf("%[^\n]",a)'\n' 保留在 stdin。)
猜你喜欢
  • 1970-01-01
  • 2021-12-16
  • 2018-09-02
  • 2015-11-24
  • 2022-12-10
  • 2015-01-31
  • 2022-02-10
  • 2018-11-20
  • 1970-01-01
相关资源
最近更新 更多