繁体   English   中英

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

[英]C Binary File not updating/reading

下面提供了我的代码。 我要根据提供的帐号“存入”钱。 名称,帐号和余额都在以下“ 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;
}

这种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 +”创建它。

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

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

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

如果您将在此实现中原谅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;    
}

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

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

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

我的建议是:

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM