【问题标题】:Why is my file output overwritten?为什么我的文件输出被覆盖?
【发布时间】:2015-05-30 01:53:38
【问题描述】:

我的程序接受用户输入并将其存储在我定义为结构的 Records 数组中:struct Record
用户输入是结构的字段。一切都没有错误,但似乎我无法正确格式化。我的程序一直要求用户输入,直到用户在询问是否还有记录时输入“n”。
一旦没有更多记录,程序将循环通过创建的记录和文件打印它们中的每一个,并用制表符隔开,最后以换行符开始下一条记录。但是,它不是从新行开始并以相同的方式打印另一条记录,而是覆盖先前打印的记录并进一步制表下一条记录。
是什么导致这种情况发生?

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

struct Record
{
    char fname[51];
    char lname[51];
    char address[51];
    char city[51];
    char state[51];
    char zipcode[51];
    char phoneNumber[51];
};

int main()
{
    FILE *fileWriter;
    const char filename[] = "data.txt";
    char answer = 'y';
    int size = 1;
    int i = 0;
    struct Record *records;
    struct Record *records_temp = NULL;

    while(answer == 'y' || answer == 'Y')
    {
        struct Record *records_temp = calloc((size),sizeof(*records));         
        records = records_temp;
        printf("First Name: \n");
        scanf("%s", records[size-1].fname);

        printf("Last Name: \n");
        scanf("%s", records[size-1].lname);

        printf("Address: \n");
        scanf(" %[^\n]", records[size-1].address);

        printf("City: \n");
        scanf("%s", records[size-1].city);

        printf("State: \n");
        scanf("%s", records[size-1].state);

        printf("Zipcode: \n");
        scanf("%s", records[size-1].zipcode);

        printf("Phone Number: \n");
        scanf("%s", records[size-1].phoneNumber);
        //stores all record info

        printf("Are there anymore records? [y/n] ");
        scanf(" %c", &answer);
        if(answer == 'y' || answer == 'Y')
        {
            size++;
            printf("\n");
        }
    }
        //open file

    fileWriter = fopen(filename,"wb");

    if(fileWriter != NULL)
    {
        for(;i< size; i++)
        {
            fprintf(fileWriter,"%s\t",records[i].fname);
            fprintf(fileWriter,"%s\t",records[i].lname);
            fprintf(fileWriter,"%s\t",records[i].address);
            fprintf(fileWriter,"%s\t",records[i].city);
            fprintf(fileWriter,"%s\t",records[i].state);
            fprintf(fileWriter,"%s\t",records[i].zipcode);
            fprintf(fileWriter,"%s\n",records[i].phoneNumber);
        }
        free(records);
        fclose(fileWriter);
    }
    else
    {
        printf("Error opening file.");   
    }
}

【问题讨论】:

  • 请将问题简化为一个最小示例。您可以删除很多代码。这让其他人更愿意阅读它。

标签: c file struct formatting


【解决方案1】:

我稍微修改了你的代码,但我认为你应该在这里使用链表作为数据结构,它更简单,消耗更少的内存。 我做了一些尝试,一切顺利。 :) 希望对你有帮助!!

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

typedef struct Record Record;

struct Record
    {
        char fname[51];
        char lname[51];
        char address[51];
        char city[51];
        char state[51];
        char zipcode[51];
        char phoneNumber[51];

        Record *next;
    };


int main()
{
    FILE *fileWriter;
    const char filename[] = "data.txt";
    char answer = '\0';
    // int size = 1;
    // int i = 0;
    Record *records = NULL;
    Record *records_first = NULL;
    Record *records_previous = NULL;

    fileWriter = fopen(filename,"wb");

    if(fileWriter != NULL) {

        for( ; ; ) {
            records = (Record*) malloc(sizeof(Record));  

            if(records_first == NULL)
                records_first = records;

            if(records_previous != NULL)
                records_previous->next = records;

            records = records_first;
            printf("First Name: \n");
            scanf("%s", records->fname);
            fprintf(fileWriter,"%s\t",records->fname);

            printf("Last Name: \n");
            scanf("%s", records->lname);
            fprintf(fileWriter,"%s\t",records->lname);

            printf("Address: \n");
            scanf(" %[^\n]", records->address);
            fprintf(fileWriter,"%s\t",records->address);

            printf("City: \n");
            scanf("%s", records->city);
            fprintf(fileWriter,"%s\t",records->city);

            printf("State: \n");
            scanf("%s", records->state);
            fprintf(fileWriter,"%s\t",records->state);

            printf("Zipcode: \n");
            scanf("%s", records->zipcode);
            fprintf(fileWriter,"%s\t",records->zipcode);

            printf("Phone Number: \n");
            scanf("%s", records->phoneNumber);
            fprintf(fileWriter,"%s\t\n\n",records->phoneNumber);

            records->next = NULL;
            records_previous = records;

            printf("Are there anymore records? [y/n] ");
            scanf(" %c", &answer);

            if(tolower(answer) != 'y') {
                free(records);
                fclose(fileWriter);
                break;
            }
        }

    } else
        printf("Error opening file.");

    return 0;
}

【讨论】:

  • typedef语句的目的是什么?
  • @Karlioh:到目前为止,使用typedef 是为了方便不必在任何地方写struct Record,而是您可以简单地写Record,例如Record *next; struct Record *next;
  • 用户输入的格式不正确,换行符似乎不起作用
【解决方案2】:

使用附加的a 模式打开文件,如“附加”。我认为没有必要进一步解释这有什么好处,对吧?

但是,实际问题似乎是您已经在输入循环中覆盖了指向上一条记录的指针。那应该如何工作?只需通过该循环并尝试按照程序的说明进行操作即可。

问题是你不能这样做。对分配的块使用链表。

【讨论】:

  • 你的意思是使用“wa”还是“wba”,或者我到底在说什么?
  • @Karlioh:“附加”有什么不清楚的地方?你明白b是什么模式吗?如果没有,你应该谷歌或者 - 更好 - 买一本好书。
  • 所以我已经阅读了文件打开模式并意识到我的文本文件不需要 b,但是当我将 a 添加到“w”并使其变为“wa”时,我得到了显示“调试断言失败”的错误
  • 您没有充分了解文件模式。对于文本文件,您实际上需要“t” - 取决于操作系统,但比抱歉更安全。好吧,我已经写了许多其他错误。你的代码。实际问题已经在他的输入循环中......等等 - 我不是已经写了吗?哦是的。所以请阅读。
【解决方案3】:
while(answer == 'y' || answer == 'Y')
{
    struct Record *records_temp = calloc((size),sizeof(*records));

    records = records_temp;
    ...
}

你也许是这个意思?

while(answer == 'y' || answer == 'Y')
{
    struct Record *records_temp = realloc(records, size * sizeof *records);
    if (records_temp == NULL)
    {
         /* Handle allocation error */
    }
    records = records_temp;
    /* ... */
}

不要将callocrealloc 混淆。如果您需要进一步说明,请阅读手册。

别忘了将records初始化为NULL...


如果您关心的是优化,那么这里最重要的瓶颈将是您的文件输入/输出。这是不可避免的,除了研究setvbuf 之外,您无能为力。下一个瓶颈将是对内核分配函数的底层调用。您可以通过减少调用分配函数来减少该瓶颈。例如,您可以通过每次将其大小增加一倍而不是添加 1 来扩大您的数组:

size_t size = 0;
int answer;
do {
    size_t index = size++;
    if ((index & size) == 0) {
        void *temp = realloc(array, (2 * index + 1) * sizeof *array);
        if (temp == NULL) {
            /* Handle allocation error */
        }
        array = temp;
    }

    puts("First Name:");
    scanf("%s", array[index].fname);

    /* snip */

    answer = getchar();
} while (answer != EOF && tolower(answer) == 'y');

或者,您可以重新发明轮子并执行 realloc 在幕后执行的相同工作(并且可能会失去一些优化的好处),方法是向您的代码添加对 memcpyfree 的调用,如下所示:

while(answer == 'y' || answer == 'Y')
{
    struct Record *records_temp = calloc((size),sizeof(*records));
    if (records_temp == NULL)
    {
        /* Handle allocation error */
    }
    if (records != NULL)
    {
        memcpy(records_temp, records, (size - 1) * sizeof *records);
        free(records);
    }
    records = records_temp;
    /* ... */
}

附:万一你第一次错过了:别忘了将records初始化为NULL...

【讨论】:

  • 那么这会比链表表现更好吗?每回合复制所有元素?碎片记忆?甚至“重新发明轮子”。太好了!
  • @Olaf 此代码不是高效,但可以很容易地提高效率......这个答案介绍了解决问题所需的最低限度的逻辑。是的。与引入不必要的逻辑、额外的测试用例和可能的进一步问题相比,这是一个更好的答案。
  • 你来了,@Olaf。我介绍了一种有效的模式。请告诉我有效的模式如何比您的链表建议更“碎片化记忆”。
【解决方案4】:

我看到的最大问题是内存分配逻辑。第一次通过循环,您为 1 条记录分配内存并增加大小。第二次循环,你为另外 2 条记录分配内存,因为 size == 2。第三次循环,你为另外 3 条记录分配内存,总共 1+2+3=6。

这回答了为什么会发生这种情况。 Olaf 的建议修复,链表,是一个很好的解决方案。

【讨论】:

  • 为什么需要链表来解决这个问题?
  • @undefined behavior 必要是一个强词。 ;-) 简单、简单、实用更适合。但严格来说,链表不是必需的。事先不知道尺寸,必须有一种方法可以随时适应。链表是一种方法,而 realloc() 听起来也是另一种好方法。
  • 我很难看出引入不必要的逻辑是“容易”、“简单”还是“有用”。澄清:“为什么需要链表?”意思是“为什么我们需要能够任意重新排列元素?”而“链表是不必要的”意味着“链表在这里不合适,因为没有证据表明我们需要能够任意重新排列元素。额外的逻辑是不必要的。'如果我们所有的程序都是这样的,那么……看看它在 GNOME 上的进展如何。
  • @undefinedbehaviour:链表是编程中最基本但最通用的结构之一(嗯 - C 级编程)。既不复杂,也不需要“随意插入”。放置和获取队列操作就足够了。您有什么简单、快速和紧凑的建议?批评很容易,除非你必须想出一个更好的主意。轮到你了!
  • 我在这里同意@Olaf。与realloc() 的动态内存分配相比,链表看起来是一个可能的解决方案。也就是说,链表实现的复杂性取决于编写代码的开发人员。对于在简单循环中犯逻辑错误的初学者来说,链表是一个很长的路要走。
猜你喜欢
  • 1970-01-01
  • 2021-02-21
  • 1970-01-01
  • 2011-09-22
  • 2015-07-31
  • 1970-01-01
  • 2020-03-06
  • 1970-01-01
  • 2013-01-25
相关资源
最近更新 更多