C二进制文件未更新/读取

时间:2018-07-27 14:32:41

标签: c file

下面我提供了我的代码。我要根据提供的帐号“存入”钱。名称,帐号和余额都在以下“ accounts.dat”文件中。

Herman T Travis 3 $500
Sam L Travis 1 $40
Henry O Billiam 2 $6000

我不确定创建文件的方式(使用简单的vi编辑器)还是代码中的问题,但是当我运行该文件并将帐号和余额提供给程序时,不会报告该错误。文件中的新余额。有什么建议吗?

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

typedef struct {
    char first[15];
    char middle[2];
    char last[15];
    float balance;
    long int acct;
} data;

int main() {
    int choice = -1, i = 0, n = 0;
    long int number;
    double amount;
    FILE *aPtr;
    data accounts[50];

    aPtr = fopen("accounts.dat", "ab+");/* Opens file for read/write; appends to file if exist */

    if (aPtr == NULL) {
        printf("File could not be opened");
        exit(1);
    }

    fseek(aPtr, 0, SEEK_SET); /* Moving pointer to start of file*/

    while (fread(accounts + 1, sizeof(data), 1, aPtr) == 1) /* Read and store info in file, into accounts array */

        i++;
        n = i; /* Num. of records in file */

        do {
            printf("Select Option\n" );
            printf("0: Exit\n1: Deposit\n2: Withdrawl\n3: Add Account\n4: Remove Account\n5: Balance Inquiry\n6: View Accounts\n: ");
            scanf("%d", &choice);

            switch (choice) {
              case 0: /* Exit */
                fclose(aPtr);
                break;

              case 1: /* Deposit*/
                fseek(aPtr, 0, SEEK_SET);
                printf("Enter account number:\n");
                scanf("%ld", &number);
                printf("Enter amount to be deposited:\n");
                scanf("%lf", &amount);
                for (i = 0; i < n; i++) {
                    if (accounts[i].acct == number) {
                        accounts[i].balance = accounts[i].balance + amount;
                        fseek(aPtr, i * sizeof(data), SEEK_SET); /* Pointer goes to accountlocation in file*/
                        fwrite(accounts + i, sizeof(data), 1, aPtr); /* Write modified account into file */
                        break;
                    }
                }
                if (i == n) {
                    printf ( "Account does not exist\n" );
                }
                break;
            }
        } while (choice != 0);

    return 0;
}

4 个答案:

答案 0 :(得分:0)

while循环读取数据不正确

while( fread(accounts+1, sizeof(data), 1, aPtr) == 1 ) /* Read and store info in file, into accounts array */
    i++;

它只是读入accounts[1],所以无论文件中有多少个帐户,数组中都只有一个。

您要做的是将每条记录读入accounts[i]中,这是下面的代码。

while( fread(&accounts[i], sizeof(data), 1, aPtr) == 1 ) /* Read and store info in file, into accounts array */
    i++;

或者更好,如果您计算出文件的大小

fseek(aPtr, 0, SEEK_END);
unsigned long len = (unsigned long)ftell(aPtr);

那么您知道len除以sizeof(data)是多少个记录

n = len/sizeof(data);

您可以一次性读取所有记录。

if(fread(accounts, sizeof(data), n, aPtr) != n)
  {
  /* Oops! Did not read everything in */
  }

这具有您可以使用malloc的附加好处,因此您不必设置50个帐户的硬限制。

打开文件的方式也不正确,因为无论您用fseek做什么,它都会将您写入的内容附加到文件末尾。您需要改用“ rb +”,但是如果文件不存在,则需要首先使用“ wb +”创建它。

答案 1 :(得分:0)

正确填充结构是此答案的重点。

文件的命名约定表示该文件应为二进制文件(即.dat在业界通常用于二进制文件)。但是您显示的内容清楚地表明它是一个文本文件,因此在以下示例中,我使用data.txt作为文件名。而且,由于它是一个文本文件,并且包含定义明确的行字段,因此将内容解析为struct会很简单

文件的格式是严格定义的,并使用空格作为分隔符。没关系,但是选择一个在字段内容中通常不使用的可见分隔符(例如逗号:,)会在不使用字段(例如没有中间名)时使事情变得容易。但这不是必需的,下面的示例将使用空格分隔符。

如果您将在此实现中避免使用fgets()strtok()(几乎没有错误检查/处理),则提供了一个使用数据文件内容填充结构的示例:

typedef struct
{
    char first[15];
    char middle[2];
    char last[15];
    float balance;
    long int acct;
}DATA;
const char filename[] = {".\\data.txt"}; 

int lineCount(const char *fname);
DATA * populateData(int lines, const char *fname);

int main(void)
{
    int lines = lineCount(filename);//count number of accounts in file (lines in file)
    DATA *data = populateData(lines, filename);//Create and populate data structure
    if(data)
    {
        ;   //use data here
        free(data); //free data memory when finished using it.
    }

    return 0;
}


int lineCount(const char *fname)
{
    int count=0;
    char line[260];
    FILE *fp = fopen(fname, "r");
    if(fp)
    {
        while(fgets(line, 260, fp)) count++;
        fclose(fp);
    }
    return count;
}

DATA * populateData(int lines, const char *fname)
{
    int i;
    char *tok;
    char *endPtr;
    char line[260];
    DATA *data = calloc(lines, sizeof(*data ));
    if(data)
    {
        FILE *fp = fopen(fname, "r");
        if(fp)
        {
            for(i=0;i<lines;i++)
            {
                if(fgets(line, 260, fp))
                {
                    //get first name
                    tok = strtok(line, " ");
                    if(tok)
                    {
                        strcpy(data[i].first, tok);
                        //get Middle name
                        tok = strtok(NULL, " ");
                        if(tok)
                        {
                            strcpy(data[i].middle, tok);
                            //get last name
                            tok = strtok(NULL, " ");
                            if(tok)
                            {
                                strcpy(data[i].last, tok);
                                //get balance
                                tok = strtok(NULL, " ");
                                if(tok)
                                {
                                    data[i].acct = atoi(tok);
                                    //get acct
                                    tok = strtok(NULL, "$");
                                    if(tok)
                                    {
                                        data[i].balance = strtod(tok, &endPtr);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            fclose(fp);
        }
    }

    return data;    
}

答案 2 :(得分:0)

您的程序中存在一个主要错误:

while (fread(accounts + 1, sizeof(data), 1, aPtr) == 1) /* Read and store info in file, into accounts array */

没有{来将所有语句括在一个块中。因此,i++;语句中仅重复第一个(while),其余仅执行一次。

我的建议是:

  • 不要在一行的末尾写注释,它们会使程序的可读性降低。
  • 使用{}将所有命令语句括在块中。
  • {ifwhile放在同一行,以用于and switch`语句(这种样式称为 Kernighan和Ritchie < / em>)。
  • 不要在一行中以if (i == n) { printf("Account does not exist\n"); }的形式包装语句

关于程序的目标,您无法轻松地尝试更新文本文件。要么将整个文件读入内存结构,然后从内存中写入更新的内容,要么使用具有固定宽度字段的二进制格式。

Unix的哲学是偏爱前一种方法,而将后者保留给具有高级API的大型数据库,以正确解决并发访问问题。

答案 3 :(得分:0)

还要在写入文件时添加fflush。在fflush()上查看您的文档。