你如何允许使用scanf输入空格?

Posted

技术标签:

【中文标题】你如何允许使用scanf输入空格?【英文标题】:How do you allow spaces to be entered using scanf? 【发布时间】:2010-11-17 21:25:52 【问题描述】:

使用以下代码:

char *name = malloc(sizeof(char) + 256); 

printf("What is your name? ");
scanf("%s", name);

printf("Hello %s. Nice to meet you.\n", name);

用户可以输入他们的名字,但是当他们输入一个带有空格的名字时,Lucas Aardvarkscanf() 只会切断Lucas 之后的所有内容。如何让scanf() 允许空格

【问题讨论】:

请注意,更惯用的是 'malloc(sizeof(char) * 256 + 1)' 或 'malloc(256 + 1)',甚至更好(假设 'name' 将严格在本地使用) '字符名[256+1]'。 '+1' 可以作为空终止符的助记符,它需要包含在分配中。 @Barry - 我怀疑sizeof(char) + 256 是一个错字。 【参考方案1】:

这个例子使用了一个倒置的扫描集,所以 scanf 一直在取值直到它遇到一个 '\n'-- 换行符,所以空格也被保存了

#include <stdio.h>

int main (int argc, char const *argv[])

    char name[20];

    // get up to buffer size - 1 characters (to account for NULL terminator)
    scanf("%19[^\n]", name);
    printf("%s\n", name);
    return 0;

【讨论】:

小心缓冲区溢出。如果用户写了一个 50 个字符的“名字”,程序很可能会崩溃。 既然知道缓冲区大小,可以使用%20[^\n]s来防止缓冲区溢出 45 分,没有人指出s 存在明显的货物崇拜!【参考方案2】:
/*reading string which contains spaces*/
#include<stdio.h>
int main()

   char *c,*p;
   scanf("%[^\n]s",c);
   p=c;                /*since after reading then pointer points to another 
                       location iam using a second pointer to store the base 
                       address*/ 
   printf("%s",p);
   return 0;
 

【讨论】:

你能解释一下为什么这是正确的答案吗?请不要发布仅代码的答案。 s 在扫描集之后不属于那里【参考方案3】:

人们(以及尤其是初学者)绝不应该使用scanf("%s")gets() 或任何其他没有缓冲区溢出保护的函数,除非您确定输入将始终为一种特定的格式(甚至可能不是)。

请记住,scanf 代表“扫描格式”,并且与用户输入的数据相比,更少的格式非常宝贵。如果您可以完全控制输入数据格式但通常不适合用户输入,这是理想的选择。

使用fgets()具有缓冲区溢出保护)将您的输入转换为字符串,并使用sscanf() 对其进行评估。由于您只需要用户输入的内容而不进行解析,因此在这种情况下您实际上并不需要 sscanf()

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Maximum name size + 1. */

#define MAX_NAME_SZ 256

int main(int argC, char *argV[]) 
    /* Allocate memory and check if okay. */

    char *name = malloc(MAX_NAME_SZ);
    if (name == NULL) 
        printf("No memory\n");
        return 1;
    

    /* Ask user for name. */

    printf("What is your name? ");

    /* Get the name, with size limit. */

    fgets(name, MAX_NAME_SZ, stdin);

    /* Remove trailing newline, if there. */

    if ((strlen(name) > 0) && (name[strlen (name) - 1] == '\n'))
        name[strlen (name) - 1] = '\0';

    /* Say hello. */

    printf("Hello %s. Nice to meet you.\n", name);

    /* Free memory and exit. */

    free (name);
    return 0;

【讨论】:

我不知道fgets()。它实际上看起来比scanf() 更容易使用。 +1 如果你只是想从用户那里得到一条线,那就更容易了。它也更安全,因为您可以避免缓冲区溢出。 scanf 系列对于将字符串转换为不同的东西非常有用(例如四个字符和一个 int,例如使用 "%c%c%c%c%d"),但是即使那样,您也应该使用 fgets 和 sscanf,而不是scanf,避免缓冲区溢出的可能性。 您可以将最大缓冲区大小放在 scanf 格式中,您不能在运行时不构建格式的情况下放入运行时计算的一个(对于 printf,* 不等效,* 是有效的修饰符对于具有另一种行为的 scanf:抑制分配)。 另请注意,如果数字转换溢出,scanf 的行为未定义(N1570 7.21.6.2p10,最后一句,自 C89 以来的措辞不变),这意味着 nonescanf 函数可以安全地用于不可信输入的数字转换。 @JonathanKomar 和其他任何人在未来阅读这篇文章:如果你的教授告诉你必须在作业中使用 scanf,他们这样做是错误的,你可以告诉他们我说过,如果他们想和我争论,我的电子邮件地址很容易从我的个人资料中找到。【参考方案4】:

如果有人还在寻找,这对我有用 - 读取任意长度的字符串,包括空格。

感谢网络上的许多海报分享这个简单而优雅的解决方案。 如果它有效,功劳归于他们,但任何错误都是我的。

char *name;
scanf ("%m[^\n]s",&name);
printf ("%s\n",name);

【讨论】:

值得注意的是,这是一个 POSIX 扩展,在 ISO 标准中不存在。为了完整起见,您可能还应该检查errno 并清理分配的内存。 s 在扫描集之后不属于那里【参考方案5】:

你可以用这个

char name[20];
scanf("%20[^\n]", name);

或者这个

void getText(char *message, char *variable, int size)
    printf("\n %s: ", message);
    fgets(variable, sizeof(char) * size, stdin);
    sscanf(variable, "%[^\n]", variable);


char name[20];
getText("Your name", name, 20);

DEMO

【讨论】:

我没有测试,但根据此页面中的其他答案,我相信您的示例中 scanf 的正确缓冲区大小为:scanf("%19[^\n]", name);(仍为简洁答案 +1) 顺便说一句,sizeof(char) 根据定义总是为 1,因此无需乘以它。 @paxdiablo 我不认为这适用于所有架构/平台 @Vitim.us:如果您指的是我的sizeof(char) == 1 评论,这是标准规定的。例如,参见C11 6.5.3.4 /4:“当sizeof 应用于类型为charunsigned charsigned char(或其限定版本)的操作数时,结果为1”。有些人犯了一个错误,因为 sizeof 返回类型/变量中的字节数,所以 16 位 char 将给出两个。但事实并非如此,因为标准并未将“字节”定义为 8 位,而是将其定义为“连续的位序列,其数量由实现定义”。【参考方案6】:

虽然您确实不应该使用 scanf() 来处理这类事情,因为有更好的调用方式,例如 gets()getline(),但可以这样做:

#include <stdio.h>

char* scan_line(char* buffer, int buffer_size);

char* scan_line(char* buffer, int buffer_size) 
   char* p = buffer;
   int count = 0;
   do 
       char c;
       scanf("%c", &c); // scan a single character
       // break on end of line, string terminating NUL, or end of file
       if (c == '\r' || c == '\n' || c == 0 || c == EOF) 
           *p = 0;
           break;
       
       *p++ = c; // add the valid character into the buffer
    while (count < buffer_size - 1);  // don't overrun the buffer
   // ensure the string is null terminated
   buffer[buffer_size - 1] = 0;
   return buffer;


#define MAX_SCAN_LENGTH 1024

int main()

   char s[MAX_SCAN_LENGTH];
   printf("Enter a string: ");
   scan_line(s, MAX_SCAN_LENGTH);
   printf("got: \"%s\"\n\n", s);
   return 0;

【讨论】:

有一个原因为什么 gets 被弃用并从标准中删除 (***.com/questions/30890696/why-gets-is-deprecated)。 scanf 甚至更糟糕,因为至少后者有办法使其安全。 Why is the gets function so dangerous that it should not be used? gets 早在 2016 年之前就已从 C99 中弃用。它已从 C11 中完全删除【参考方案7】:

试试

char str[11];
scanf("%10[0-9a-zA-Z ]", str);

希望对您有所帮助。

【讨论】:

(1) 显然要接受空格,需要在字符类中放一个空格。 (2) 请注意,10 是将读取的最大字符数,因此 str 必须至少指向大小为 11 的缓冲区。 (3) 这里最后的 s 不是格式指令,但 scanf 会在这里尝试完全匹配它。该效果将在 1234567890s 之类的条目上可见,其中 s 将被消耗但没有放在任何位置。不会消耗另一封信。如果在 s 后面加上另一种格式,则只有在有要匹配的 s 时才会读取。 另一个潜在的问题,在第一个或最后一个以外的其他地方使用 - 是实现定义的。通常,它用于范围,但范围指定的内容取决于字符集。 EBCDIC 在字母范围内有漏洞,即使假设 ASCII 派生字符集,认为所有小写字母都在 a-z 范围内是天真的...... "%[^\n]" 和gets()有同样的问题,缓冲区溢出。带有未读取 \n final 的附加问题;这将被大多数格式从跳过空格开始的事实所掩盖,但 [ 不是其中之一。我不明白使用 scanf 读取字符串的实例。 从输入字符串的末尾删除了s,因为在某些情况下它既是多余的又是不正确的(正如前面的 cmets 所指出的)。 [自己的 格式说明符,而不是 s 格式说明符的一些变体。【参考方案8】:

你可以使用scanf 来达到这个目的,但需要一点小技巧。实际上,您应该允许用户输入,直到用户点击 Enter (\n)。这将考虑每个字符,包括 空格。这是一个例子:

int main()

  char string[100], c;
  int i;
  printf("Enter the string: ");
  scanf("%s", string);
  i = strlen(string);      // length of user input till first space
  do
  
    scanf("%c", &c);
    string[i++] = c;       // reading characters after first space (including it)
   while (c != '\n');     // until user hits Enter
  string[i - 1] = 0;       // string terminating
return 0;

这是如何工作的?当用户从标准输入中输入字符时,它们将存储在 string 变量中,直到第一个空格。之后,其余条目将保留在输入流中,并等待下一个 scanf。接下来,我们有一个for 循环,它从输入流中逐个字符地获取(直到\n)并将它们附加到 string 变量的末尾,从而形成一个与用户从键盘输入相同的完整字符串.

希望这会对某人有所帮助!

【讨论】:

缓冲区溢出。【参考方案9】:

您可以使用fgets() 函数读取字符串或使用scanf("%[^\n]s",name);,这样字符串读取将在遇到换行符时终止。

【讨论】:

小心这并不能防止缓冲区溢出 s 不属于那里【参考方案10】:

getline()

现在是 POSIX 的一部分,毫无疑问。

它还处理了您之前询问的缓冲区分配问题,尽管您必须处理 freeing 内存。

【讨论】:

标准?在您引用的参考资料中:“getline() 和 getdelim() 都是 GNU 扩展。” POSIX 2008 添加了 getline。所以 GNU 在 2.9 版左右为 glibc 更改了头文件,这给许多项目带来了麻烦。不是一个确定的链接,但请看这里:bugzilla.redhat.com/show_bug.cgi?id=493941。至于联机手册页,我抓了 google 找到的第一个。【参考方案11】:

不要在未指定字段宽度的情况下使用scanf() 读取字符串。您还应该检查返回值是否有错误:

#include <stdio.h>

#define NAME_MAX    80
#define NAME_MAX_S "80"

int main(void)

    static char name[NAME_MAX + 1]; // + 1 because of null
    if(scanf("%" NAME_MAX_S "[^\n]", name) != 1)
    
        fputs("io error or premature end of line\n", stderr);
        return 1;
    

    printf("Hello %s. Nice to meet you.\n", name);

或者,使用fgets():

#include <stdio.h>

#define NAME_MAX 80

int main(void)

    static char name[NAME_MAX + 2]; // + 2 because of newline and null
    if(!fgets(name, sizeof(name), stdin))
    
        fputs("io error\n", stderr);
        return 1;
    

    // don't print newline
    printf("Hello %.*s. Nice to meet you.\n", strlen(name) - 1, name);

【讨论】:

以上是关于你如何允许使用scanf输入空格?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 scanf 从用户输入中获取字符串( char * ),由双引号分隔并包含空格 [关闭]

〔杂谈〕关于scanf方法输入字符类型需要加空格问题分析

C语言用如何用scanf输入带有空格的字符串

你了解gets()和scanf()吗

C 不能输入带空格的字符串

为啥不能用scanf读入一个含有空格的字符串