带空格的字符串输入

3
我正在尝试在基本的Ubuntu GCC编译器中运行以下代码,这是一个基本的C类。
#include<stdio.h>

struct emp
{

  int emp_num, basic;
  char name[20], department[20];

};

struct emp read()
{
  struct emp dat;

  printf("\n Enter Name : \n");
  scanf("%s", dat.name);

  printf("Enter Employee no.");
  scanf("%d", &dat.emp_num);

  //printf("Enter department:");
  //fgets(dat->department,20,stdin);

  printf("Enter basic :");
  scanf("%d", &dat.basic);

  return dat;
}

void print(struct emp dat)
{
  printf("\n Name : %s", dat.name);

  printf("\nEmployee no. : %d", dat.emp_num);

  //printf("Department: %s", dat.department);

  printf("\nBasic : %d\n", dat.basic);
}

int main()
{
  struct emp list[10];

  for (int i = 0; i < 3; i++)
  {
    printf("Enter Employee data\n %d :\n", i + 1);
    list[i] = read();
  }

  printf("\n The data entered is as:\n");

  for (int i = 0; i < 3; i++)
  {
    print(list[i]);
  }

  return 0;
}

我希望名称可以包含空格。
当我输入结构的值时,问题就出现了。我能够第一次输入名称,但是随后的迭代甚至不提示我输入。
我尝试使用 fgetsscanf("%[^\n]",dat.name) 甚至 gets()(我很绝望),但每次都面临着相同的问题。
第一个结构的输出很好,但是对于其余的结构,输出要么是垃圾,要么是人的姓氏,要么就是空白。
有什么想法吗?

1
如果您确实需要从用户处“扫描”“字符串”,请使用%Ns,其中N被替换为描述要读入的缓冲区大小的整数。在OP的示例中,这将是%19s,留出必要的“0”终止符。这样做可以避免潜在的缓冲区溢出。 - alk
但是scanf不会读取空格,我希望人们能够输入他们的全名。 - Suman Roy
所以你可以切换到 fgets() 或者甚至是 read(),并自己解析已输入的内容。 - alk
3个回答

3
使用 scanf("%s") 读取字符串时,只能读取到第一个空格字符。因此,你的字符串不能包含空格。相反,你可以使用 fgets 方法,它会一直读取到第一个换行符。
另外,如果要清空输入缓冲区,建议使用例如 scanf("%d\n") 而不是仅使用 scanf("%d")。否则,下一个 fgets 将读取换行符并不会提示你输入。
我建议你尝试编写一个小程序,先读取一个整数,然后再读取一个字符串。这样你就能理解我的意思,也更容易进行调试。如果你有问题,请发布一个新的问题。

你能具体说明一下吗,因为我认为我的问题与缓冲区有关。而且就像我说的,我已经尝试使用fgets(),但结果相同。 - Suman Roy
如果您期望两个输入分别在两行中给出,那么在读取整数和字符串之间需要清空缓冲区。可以通过 scanf("%d\n", &n); 实现。由于我认为您可能不太理解输入缓冲区的工作原理,请先尝试编写一个小程序进行实验。 - nickie
但如果我这样做,我必须在我实际需要的值之间输入一个额外的值。如果在“printf(“输入员工编号。”)”之前再放入另一个“scanf()”,那么我会在所有错误的点上被提示数据。 - Suman Roy
1
看,我讨厌做别人的练习题。这不一定是你的情况,但我感觉是。我认为这一切的重点在于被引导到正确的方向,而不是被给予手头一个非常简单问题的解决方案。 - nickie
@user2737039 还可以尝试使用另一个 fgets() 调用,然后使用 strtol() 获取整数,而不是在 scanf() 上瞎搞。scanf() 函数非常反直觉,肯定不容易使用,所有初学者最好远离它。 - user529758

3
问题在于scanf("%[^\n",..fgets不会跳过前一行读取时可能留下的任何空格。特别是,它们不会跳过上一行末尾的换行符,因此如果该换行符仍然存在于输入缓冲区中(当上一行使用scanf("%d",..读取时),scanf将无法读取任何内容(在name数组中留下随机垃圾),而fgets将只读取换行符。

最简单的解决方法是在scanf中添加一个显式空格来跳过空格:

printf("\n Enter Name : \n");
scanf(" %19[^\n]", dat.name);

这也会跳过行首的任何空格(和空行),因此如果您想要以空格开头的名称可能会有问题。

请注意,我还添加了一个长度限制19,以避免溢出名称数组 - 如果用户输入更长的名称,则其余部分将留在输入上并被读取为员工号码。您可能希望跳过该行的其余部分:

scanf("%*[^\n]");

这将读取输入中剩余的任何非换行符字符并将其丢弃。您可以与先前的scanf组合使用,从而获得类似下面的代码:
printf("\n Enter Name : ");
scanf(" %19[^\n]%*[^\n]", dat.name);
printf("Enter Employee no. : ");
scanf("%d%*[^\n]", &dat.emp_num);
printf("Enter department : ");
scanf(" %19[^\n]%*[^\n]", dat.department);
printf("Enter basic : ");
scanf("%d%*[^\n]", &dat.basic);

这将忽略任何人在行上输入的多余内容,但是仍然会遇到某些人在期望数字的位置输入字母或文件结束的情况。为了解决这些问题,您需要检查scanf的返回值。

抱歉,我尝试了但仍然遇到同样的问题;我仍然没有收到下一个名称的提示。 - Suman Roy
@user2737039:拿你发布的代码,将scanf中的"%s"改为" %19[^\n]",只要你不输入太长的名字,它就可以正常工作。别忘了空格——这非常重要。 - Chris Dodd
不确定为什么在某些 scanf 中有 %*[^\n]。它们会失败,因为如果要读取的第一个字符是 \n,则 %[^\n] 格式说明符将失败。 - Spikatrix
@CoolGuy:这些是用来消耗并丢弃到下一个换行符的所有内容。如果没有任何内容,它们将不会执行任何操作,但如果在该行上留下了随机垃圾(如果输入的字符串过长或数字后面有无用信息),则会被丢弃。 - Chris Dodd
啊,是的。你说得对。忘记考虑输入过长的情况了。我的错。 - Spikatrix

0
你尝试过的是:-
scanf("%[^\n]",dat.name)

在这里您忘记了指定说明符。

您可以尝试使用以下内容:

scanf ("%[^\n]%*c", dat.name);

或者使用fgets()如果你想读取包含空格的内容。

注意:使用"%s"将会读取输入直到遇到空格为止。


实际上我尝试了两种方式,结果都一样。 - Suman Roy
你尝试使用过这个吗:scanf("%[^\n]s",dat.name);?它会一直接收值,直到遇到'\n'。 - Rahul Tripathi
我也做了那个。它会识别空格,但我没有收到输入下一个字符串的提示。 - Suman Roy
-1:[是指定符号;在它之后没有必要再加上另一个指定符号(除非你想读取其他内容)。 - Chris Dodd
@ChrisDodd:那么你的意思是 scanf ("%[^\n]%*c", dat.name); 等价于 scanf("%[^\n]",dat.name); 吗?如果我理解错了,请纠正我!! - Rahul Tripathi
@RahulTripathi:从dat.name的角度来看,这两者是等价的。唯一的区别在于第一个会读取一个额外的字符然后丢弃它(对于%*c模式而言)。 - Chris Dodd

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