Scanf始终跳过第二行输入

时间:2015-10-05 22:36:57

标签: c struct linked-list 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 <stdio.h>
#include <stdlib.h>
#include <string.h>


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;
    }
}

2 个答案:

答案 0 :(得分:1)

问题在于您的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终止是你的问题!你没有做什么..哪个会导致崩溃。你现在真的很幸运。

快速摆脱null终止符问题的一种方法是使用calloc()来分配你的节点,而不是malloc()。使用calloc(),你的内存被承诺用get go ...

中的所有空字节填充

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

答案 1 :(得分:1)

主要问题是来自先前输入'\n'的剩余"%i %f %f %i %i"保留在stdin中。执行scanf("%40[^\n]...时,不保存任何内容,'\n'仍保留在stdin中。

故事的道德:

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

  2. 切勿使用scanf()

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