简体   繁体   English

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

[英]C Binary File not updating/reading

Below I have provided my code. 下面提供了我的代码。 I am to "deposit" money based on a provided account number. 我要根据提供的帐号“存入”钱。 The name and account number and balance are all in the following 'accounts.dat' file. 名称,帐号和余额都在以下“ accounts.dat”文件中。

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

I am unsure if its the way I created my file (with a simple vi editor) or if it is a problem within my code, but when I run it and give the account number and balance to the program does not report the new balance in the file. 我不确定这是我创建文件的方式(使用简单的vi编辑器)还是代码中存在问题,但是当我运行该文件并将帐号和余额提供给程序时,不会在其中报告新余额文件。 Any suggestions? 有什么建议么?

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

This while loop to read in the data isn't right 这种while循环读取数据不正确

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

It's only reading into accounts[1] , so no matter how many accounts are in the file, you'll only have one in the array. 它只是读到accounts[1] ,所以无论文件中有多少个账户,数组中都只有一个。

What you want is to do is read each record into accounts[i] which is what the following code does. 您要做的就是将每条记录读入accounts[i] ,这是下面的代码。

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

Or better yet, if you work out the size of the file 或更妙的是,如果您计算出文件的大小

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

then you know that len divided by sizeof(data) is how many records there are 那么您知道len除以sizeof(data)就是有多少条记录

n = len/sizeof(data);

and you can read all the records in one go. 您可以一次性读取所有记录。

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

This has the added benefit that you could use malloc so that you're not setting a hard limit of 50 accounts. 这具有您可以使用malloc的额外好处,因此您不会设置50个帐户的硬限制。

Also the way you've opened the file isn't quite right as it will append anything you write to the end of the file regardless of what you do with fseek . 同样,打开文件的方式也不正确,因为无论您使用fseek做什么,它都会将您写入的所有内容附加到文件末尾。 You need to use "rb+" instead, but if the file doesn't exist, you'll need to create it first with "wb+". 您需要使用“ rb +”代替,但是如果文件不存在,则需要首先使用“ wb +”创建它。

Getting the struct populated correctly is is the focus of this answer. 正确填充该结构是此答案的重点。

Naming convention for your file indicates it should be a binary file (ie .dat is commonly used in the industry for binary files). 文件的命名约定表明它应为二进制文件(即, .dat在行业中通常用于二进制文件)。 But the contents you show clearly indicate it is a text file, so in the following example, I use data.txt as the file name. 但是您显示的内容清楚地表明它是一个文本文件,因此在以下示例中,我使用data.txt作为文件名。 And as it is a text file, and includes well defined line fields, parsing the contents into a struct will be fairly straight forward 而且由于它是一个文本文件,并且包含定义明确的行字段,因此将内容解析为一个结构将非常简单

The format of your file is strictly defined, and uses spaces as delimiters. 文件的格式是严格定义的,并使用空格作为分隔符。 This is all okay, but choosing a visible delimiter, not normally used in the field content, such as a comma: , would make things easier when a field is not used, such as when no middle name. 没关系,但是选择在字段内容中通常不使用的可见分隔符(例如逗号:) ,将在不使用字段(例如没有中间名)时使事情变得容易。 But this is not a requirement, and the following example will use space delimiters. 但这不是必需的,下面的示例将使用空格分隔符。

If you will forgive the use of fgets() and strtok() in this implementation (with little to no error checking/handling), it provides an example of populating the struct with the contents of the data file: 如果您将在此实现中原谅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;    
}

There is a major bug in your program: 您的程序中存在一个主要错误:

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

Does not have a { to enclose all the statements in a block. 没有{将所有语句括在一个块中。 Hence only the first one ( i++; ) is repeated in the while statement and the rest executes just once. 因此,在while语句中仅重复第一个( i++; ),其余的仅执行一次。

My suggestions are: 我的建议是:

  • do not write comments at the end of a lines, they tend to make the program less readable. 不要在一行的末尾写注释,它们会使程序的可读性降低。
  • use { and } to enclose all commanded statements in blocks. 使用{}将所有命令语句括在块中。
  • put the { on the same line as the if , while , for and switch` statements (this style is known as Kernighan and Ritchie ). {ifwhile ,for and switch语句放在同一行(此样式称为Kernighan和Ritchie )。
  • do not pack statements on one line as if (i == n) { printf("Account does not exist\\n"); } 语句在同一行的if (i == n) { printf("Account does not exist\\n"); } if (i == n) { printf("Account does not exist\\n"); }

Regarding your program's objective, you cannot easily update a text file as you attempt to. 关于程序的目标,您无法轻松地尝试更新文本文件。 Either read the entire file into memory structures and write the updated contents from the memory or use a binary format with fixed width fields. 要么将整个文件读入内存结构,然后从内存中写入更新的内容,要么使用具有固定宽度字段的二进制格式。

The Unix philosophy is to favor the former approach and reserve the latter for larger databases with high level APIs to address concurrent access issues properly. Unix的哲学是赞成前一种方法,而将后者保留给具有高级API的大型数据库,以正确解决并发访问问题。

Also add fflush on writing to a file. 在写入文件时还要添加fflush。 See your doc on fflush(). 在fflush()上查看您的文档。

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

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