C语言 将文件数据读入 C 中的链表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2075497/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 04:10:09  来源:igfitidea点击:

Reading File Data Into a Linked List in C

cdatabasefile-iolinked-list

提问by Nick Aberle

I am trying to create a simple phonebook program that reads data from a file and stores the content into specific nodes in the list. It works fine if I use my addEntry function with static data, such as:

我正在尝试创建一个简单的电话簿程序,该程序从文件中读取数据并将内容存储到列表中的特定节点中。如果我将 addEntry 函数与静态数据一起使用,则效果很好,例如:

addEntry("First", "Last", "555-555-5555");

If I try to read more than 1 entry from the file, each entry just appears to be whatever the last entry was in the file. For example, if my file contained:

如果我尝试从文件中读取 1 个以上的条目,则每个条目似乎都是文件中的最后一个条目。例如,如果我的文件包含:

First1
Last1
123-456-7890
First2
Last2
987-654-3210

After storing the data in the list and printing, the output would look like:

将数据存储在列表中并打印后,输出将如下所示:

First2
Last2
987-654-3210

First2
Last2
987-654-3210

Rather than printing each specific name and number. This confuses me because this issue only occurs when I read data from the file, not when I manually type in the name and number in a function call. Here are the definitions for main and addEntry, thank you in advance.

而不是打印每个特定的名称和号码。这让我很困惑,因为这个问题只发生在我从文件中读取数据时,而不是当我在函数调用中手动输入名称和数字时。这里是 main 和 addEntry 的定义,在此先感谢您。

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

struct bookNode
{
    char * firstName;
    char * lastName;
    char * phoneNumber;
    struct bookNode * next;
} * head;

FILE * fpointer;

void addEntry(char * fn, char * ln, char * pn);
void display();
int numEntries();
void writeBookData(struct bookNode * selection);

int main()
{
    head = NULL;
    addEntry("Test", "Name", "111-111-1111");
    addEntry("Test2", "Name2", "222-222-2222"); // These entries will work as intended

    int i;
    fpointer = fopen("addressbook.dat", "a+");
    if(fpointer == NULL)
    {
        printf("Error: addressbook.dat could not be opened.\n");
    }

    char first[20];
    char last[20];
    char num[20];

    while (!feof(fpointer))
    {
        fgets(first, 20, fpointer);
        fgets(last, 20, fpointer);
        fgets(num, 20, fpointer);

        //Removes newline characters from the ends of the names
        i = 0;
        while(first[i] != '\n')
        {
            i++;
        }
        first[i] = '
tempNode = (struct bookNode *)malloc(sizeof(struct bookNode));
tempNode->firstName = (char *)malloc(strlen(fn)+1);
strcpy(tempNode->firstName, fn);
'; i = 0; while(last[i] != '\n') { i++; } last[i] = '
char data[SIZE];
char *p;
/* get some useful value in data */
p = data;
'; // Adds the entry from the strings with the file data in them addEntry(first, last, num); } fclose(fpointer); display(); // typical linked list display function int entryCount = numEntries(); printf("There are %d entries in this Address Book\n", entryCount); return EXIT_SUCCESS; } void addEntry(char * fn, char * ln, char * pn) { struct bookNode * tempNode, * iterator; tempNode = (struct bookNode *)malloc(sizeof(struct bookNode)); tempNode->firstName = fn; tempNode->lastName = ln; tempNode->phoneNumber = pn; iterator = head; // If the list is empty if (head == NULL) { head = tempNode; head->next = NULL; } // The list is not empty else { while(iterator->next != NULL) { iterator = iterator->next; } tempNode->next = NULL; iterator->next = tempNode; } }

采纳答案by villintehaspam

You need to copy the string values to each new node. You are only storing the pointer to each string, but it is always the same pointers (first, last and num that are declared in main) so they all point to the same memory.

您需要将字符串值复制到每个新节点。您只存储指向每个字符串的指针,但它始终是相同的指针(在 main 中声明的 first、last 和 num),因此它们都指向相同的内存。

So in your addEntry method, you need to first allocate memory to store the string and then copy the string to the new memory.

所以在你的 addEntry 方法中,你需要先分配内存来存储字符串,然后将字符串复制到新的内存中。

Your example where you add the entries manually works because the char pointers point to static strings.

您手动添加条目的示例有效,因为字符指针指向静态字符串。

So in your addEntry method you should do something like this:

所以在你的 addEntry 方法中你应该做这样的事情:

void addEntry(char *fn, char *ln, char *pn)
{
    struct bookNode *tempNode, *iterator;

    tempNode = malloc(sizeof *tempNode);
    tempNode->firstName = malloc(strlen(fn) + 1); /* +1 for terminating 0 */
    tempNode->lastName = malloc(strlen(ln) + 1);
    tempNode->phoneNumber = malloc(strlen(pn) + 1);

    /* Omitted check for malloc failures for brevity */
    strcpy(tempNode->firstName, fn);
    strcpy(tempNode->lastName, ln);
    strcpy(tempNode->phoneNumber, pn);

    /* Now continue with what you were doing */
}

and then the same for last name and phone. Remember that you need to go through the list and free the memory for each string as well as for the nodes in the list as well.

然后姓氏和电话相同。请记住,您需要遍历列表并为每个字符串以及列表中的节点释放内存。

回答by moonshadow

Your bookNodestruct contains pointers to memory. Your addEntryfunction places a copy of these pointers into the list, but the memory they point to is still owned by the caller: it is, in fact, the first, lastand numarrays you declare in main, that you then proceed to overwrite on the next iteration of the loop.

您的bookNode结构包含指向内存的指针。你的addEntry函数将这些指针的副本进入榜单,但它们指向仍然由主叫方所拥有的记忆:它是,其实firstlastnum你在声明数组main,你再继续覆盖上的下一个迭代循环。

What your addEntryfunction needs to do instead of copying its input pointers is to allocate enough memory for the strings, copy the inputs into that memory and retain the pointers to the copies. You will also need to make sure you free all the memory you have allocated once you are done with it.

您的addEntry函数需要做的而不是复制其输入指针是为字符串分配足够的内存,将输入复制到该内存中并保留指向副本的指针。您还需要确保在完成后释放所有分配的内存。

回答by Alok Singhal

There are a couple of problems with your program.

你的程序有几个问题。

What you are doing is equivalent of:

你在做什么相当于:

#define MAX 20
struct bookNode
{
    char firstName[MAX];
    char lastName[MAX];
    char phoneNumber[MAX];
    struct bookNode *next;
} *head;

In the context of the last line, datarefers to a pointer that points to the first element of the array data(i.e., the line is equivalent to p = &data[0];). So, what you just did was to assign to the pointer pthe value of the address of the first character in data. Later, when you change the contentsof data, the pointer to the first element of datais still the same (datastill exists at the same memory location). So, all your pointers refer to the same storage, and you keep overwriting what's in the storage.

最后一行的上下文中data指的是指向数组第一个元素的指针data(即,该行等效于p = &data[0];)。所以,你刚才所做的是分配给指针p的第一个字符的地址的值data。稍后,当您更改 的内容data,指向 的第一个元素的指针data仍然相同(data仍然存在于相同的内存位置)。因此,您所有的指针都指向同一个存储,并且您不断覆盖存储中的内容。

But then why does your program work when you provide literal strings? Because each literal string in C is guaranteed to exist throughout the life of the program and has a unique address. (There is a minor exception: if you use a literal string more than once in your program, it may or may not refer to the same memory.)

但是,当您提供文字字符串时,为什么您的程序可以工作?因为 C 中的每个文字字符串都保证在程序的整个生命周期中都存在并且具有唯一的地址。(有一个小例外:如果您在程序中多次使用文字字符串,它可能会也可能不会指代同一个内存。)

So, you should dynamically allocate memory for firstName, lastName, and phoneNumbermembers of your nodes, and remember to free them when you are done with it.

所以,你应该动态分配内存firstNamelastNamephoneNumber您的节点的成员,并记住他们自由,当你用它做。

char *nl;
if ((nl = strchr(first, '\n')) != NULL) {
    *nl = '##代码##';
}

Then, you will need a corresponding freeEntryfunction to free up the space.

然后,您将需要一个相应的freeEntry函数来释放空间。

Another way to do this would be to declare your structdifferently:

另一种方法是声明你的struct不同:

##代码##

Then, your addEntryfunction doesn't need the malloc()calls for firstName, lastName, and phoneNumber, but you still need to copy data using strcpy(). (To know the reason, please refer to the link above.) Your corresponding freeEntry()function also wouldn't need to free those members.

然后,你的addEntry功能不需要malloc()通话firstNamelastName以及phoneNumber,但你仍然需要使用复制数据strcpy()。(要知道原因,请参考上面的链接。)您相应的freeEntry()功能也不需要释放这些成员。

Now, for the rest of your program. Your way of finding a terminating newline works, but you can simplify it by using strchr()standard C function. The call in your case would look like:

现在,对于程序的其余部分。您查找终止换行符的方法有效,但您可以使用strchr()标准 C 函数来简化它。您的案例中的电话如下所示:

##代码##

Finally, when you fix all the above, you will find that you are getting the last record twicein your phone book. In C, feof()doesn't tell you if you are at the end of file now: it tells you that the last attempt to read from the file failed because you wereat end of the file.

最后,当您解决上述所有问题时,您会发现您在电话簿中两次获得最后一条记录。在C语言中,feof()并没有告诉你,如果你在文件现在的结尾:它会告诉你从文件中读取最后一次尝试失败,因为你在文件末尾。