簡體   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