Scanf始终跳过第二行输入

我正在尝试读取文本数据行,并将它们存储在链表中。 输入数据看起来像这样:

David Copperfield Charles Dickens 4250 24.95 32.95 10 6 END_DATA 

我读取数据的方法是首先读取第一行,看它是否是END_DATA。 如果不是,那么我将第一行传递给一个函数,该函数创建一个带有书籍数据的链表Node。 由于某种原因,在我将第一行传递给函数后,scanf不会读取第二行。

当我尝试在读取数据后打印节点时,我的输出看起来像这样。

 David Copperfield 0 0.000000 0.000000 0 0 Charles Dickens 4250 24.950001 32.950001 10 6 Charles Dickens 0 0.000000 0.000000 0 0 

源代码如下

 #include "lab3p1.h" #include  #include  #include  struct NodeRec { Book data; struct NodeRec *next; }; typedef struct NodeRec Node; float findRevenue(Node *head); float totalWholesaleCost(Node *head); float totalProfit(Node *head); float totalBooksSold(Node *head); float averageProfitPerSale(Node *head); void printNodes(Node *head); void insertNode(Node* head, Node* newNode); Node* createNewNode(char titleS[40]) { Node* newNode; newNode = malloc(sizeof(Node)); if (newNode == NULL){ printf("ERROR ALLOCATING SPACE"); exit(-1); } else { strcpy(newNode->data.title, titleS); scanf("%40[^\n]*c", (char *)&newNode->data.author); scanf ("%i %f %f %i %i", &newNode->data.number, &newNode->data.wholesaleprice, &newNode->data.retailprice, &newNode->data.wholesalequantity, &newNode->data.retailquantity); printf ("%s \n%s \n%i \n%f \n%f \n%i \n%i\n", newNode->data.title, newNode->data.author,newNode->data.number, newNode->data.wholesaleprice, newNode->data.retailprice, newNode->data.wholesalequantity, newNode->data.retailquantity); } return newNode; } void readBooks(Node* head) { char firstline[40]; char enddataString[40] = "END_DATA"; scanf("%40[^\n]*c", (char *)&firstline); while (strcmp(firstline, enddataString)) { Node* newNode = createNewNode(firstline); insertNode(head, newNode); scanf("%40[^\n]*c", (char *)&firstline); } } void insertNode(Node* head, Node* newNode) { if (head == NULL) { head = newNode; } else { Node *preNode, *currentNode; int n = newNode->data.number; currentNode = head; while (currentNode != NULL && (currentNode->data.number) > n ) { preNode = currentNode; currentNode = currentNode->next; } newNode->next = currentNode; preNode->next = newNode; } } 

问题出在scanf格式字符串上。 在以下行中有一些问题:

 scanf("%40[^\n]*c", (char *)&firstline); 

首先,您希望读取所有行到行尾(40个字符或更少)。 %40[^\n]会这样做。 但是没有必要使用c ..括号中的表达式取而代之..所以,scanf可能只是在输入中寻找一个普通字符’c’而不是你想要的,它会导致问题您。 星号在我的位置也没有任何意义。

第二个问题是,因为你告诉scanf只消耗直到换行符的字符,所以换行符仍然在等待消耗的stdin缓冲区中。 由于下一个scanf也拒绝使用换行符,因此它根本不会消耗任何内容。 使用scanf时,必须设计格式字符串,以便在输入中分配或丢弃每个字符,否则解析将失败。

将其替换为:

 scanf("%40[^\n] ", (char *)&firstline); 

..你的程序会奏效。 这也应该在您的作者scanf调用中使用。 这里的格式字符串基本上是“读取不是换行符的所有内容..直到你看到换行符或获得40个字符。然后跳过你看到的任何空格”。 这会消耗stdin直到下一行的第一个字符,这就是你想要的。

但基本上scanf是一只熊,难以正确使用,这就是为什么它很少用于此类事情。 我从不使用它。 例如,您是否考虑过如果作者或标题包含超过40个字符,您的程序会发生什么? scanf的手册页说

当达到此最大值或找到不匹配的字符时(无论哪个先发生),读取字符都会停止。

这意味着未读的字符将保留在缓冲区中,并且会解析所有剩余的行。

当然可以设计一个可以处理它的scanf格式..你必须将它设置为使用除换行之外的所有内容然后将其丢弃..然后留下最后一个空格来使用换行符。 我现在无法让那部分工作……但是你应该:)

您遇到的另一个主要问题是使用null终止符。 首先,每个字符串必须以null结尾,并且scanf c格式说明符的手册页说明

..下一个指针必须是指向char的指针,并且必须有足够的空间容纳所有字符(不添加终止空字节)

因此,如果您告诉scanf读取40个字符,那么指向存储的指针最好指向至少41个字符! 例如:

 char author[41]; 

null终止是你的问题! 你没有做什么..哪个会导致崩溃。 你现在真的很幸运。

快速摆脱空终止符问题的一种方法是使用calloc()来分配节点,而不是malloc()。 使用calloc(),你的内存被承诺用get go中的所有空字节填充…

无论如何,我认为你现在已经足够了。 如果我是你,我会探索使用fgets()从输入中单独读取每一行,然后使用各种解析函数来获取你的值…例如atoi来解析整数。 它需要更多的工作,但最终更可靠,更容易理解。 如果您坚持使用scanf,您必须真正阅读该手册页并尝试可视化输入中的每个字符将如何被消耗或不消耗。 此外,请确保您存储的每个字符串都正确地以null结尾。 在c中,这些事情不适合你,你必须考虑它们。

主要问题是先前输入"%i %f %f %i %i"的剩余'\n'保留在stdin 。 当执行scanf("%40[^\n]... ,没有任何内容保存,并且'\n'保留在stdin

故事的道德启示:

  1. 总是检查I / O函数的结果,如fopen, fseek, fscanf, scanf, fgets, fgetc, ...

  2. 永远不要使用scanf()

使用fgets()getline()来获取一行数据。 然后解析它,随时检查意外结果。 更多的代码,但更少的时间在SO上 。