2011-04-06 36 views
1

我想在C中執行以下操作。更新c中的隨機訪問文件

我有一個包含一些記錄的隨機訪問數據文件。

這些記錄的格式如下:

Acct# First Name  Last Name Balance 
0  ""    ""    0.0 
0  ""    ""    0.0 
0  ""    ""    0.0 
05 Joe    Costanza  0.50 
0  ""    ""    0.0 
0  ""    ""    0.0 
0  ""    ""    0.0 
19 Jason   Bourne  58.00 
0  ""    ""   0.0 
0  ""    ""    0.0 
42 Andy   Der   -15.12 
0  ""    ""    0.0 
0  ""    ""    0.0 

我想從所有具有非零帳號和寫入新的更新後的餘額,以文件爲這些記錄的記錄減去結餘量。

這是我迄今爲止所嘗試完成的上述內容。

#include <stdio.h> 

struct clientData 
{ 
int acctNum;   
char lastName[15]; 
char firstName[10]; 
double balance;  
}; 

void textFile(FILE *readPtr); 

int main(void) 
{ 
    FILE *cfPtr; 

    struct clientData client = {0, "", "", 0.0}; 

    double serviceCharge = 5.0; 

    cfPtr = fopen("credit.dat", "rb+"); 

    fread(&client, sizeof(struct clientData), 1, cfPtr); 

    client.balance -= serviceCharge; 

    fseek(cfPtr,(client.acctNum - 1) * sizeof(struct clientData) 
    , SEEK_CUR); 

    fwrite(&client, sizeof(struct clientData), 1, cfPtr); 

    fclose(cfPtr); 

    return 0; 
} 

無論我嘗試什麼,我都無法將單個更新的記錄寫回文件。我甚至嘗試過沒有任何while循環的單個記錄,或者如果語句不起作用。任何人都可以告訴我我錯過了什麼。

循環:

fread(&client, sizeof(struct clientData), 1, cfPtr); 

while (!feof(cfPtr)) 
{ 
if (client.acctNum != 0) 
{ 
    client.balance -= serviceCharge; 
    fseek(cfPtr, (-1 * sizeof(struct clientData)), SEEK_CUR); 

    fwrite(&client, sizeof(struct clientData), 1, cfPtr); 
} 

fread(&client, sizeof(struct clientData), 1, cfPtr); 
} 
+2

你'cfPtr'(未聲明的;打開,但不能使用),'filePtr'(既不申報,也不能打開,但寫入)和'readPtr'(既沒有聲明也沒有打開,但從中讀取)。這段代碼不會編譯。您能否告訴我們您使用的實際代碼,並告訴我們它做了什麼以及您希望它會做什麼? – 2011-04-06 23:17:36

+0

如果大量不同名稱的文件變量指示您的代碼的某些版本試圖多次打開同一文件,請閱讀一個版本並寫入另一個版本:不要這樣做,如果您確實必須這樣做,那麼在寫入文件和嘗試讀取文件之間刷新文件。 – 2011-04-06 23:19:23

回答

2
 while (!feof(readPtr)) 
    { 
     if (client.acctNum != 0) 
     { 

在代碼中的這一點上,你有沒有閱讀什麼。沒有。 :)

您已打開文件,檢查了是否已到達文件末尾,但沒有任何代碼需要從文件中讀取任何內容,因此您的if條件只是檢查開始時的初始化結構。

如果在循環的頂端處讀取輸入,則循環輸入通常效果最佳。 (當然也有例外,但這並不像他們中的一個。)

試試這個爲出發點,爲您的循環:

while(fread(&client, sizeof(struct clientData), 1, filePtr)) { 
    if (client.acctNum) { 
     client.balance -= charge; 
     fseek(filePtr, - sizeof(struct clientData), SEEK_CUR); 
     if (1 != fwrite(&client, sizeof(struct clientData), 1, filePtr)) 
      perror("Error writing to file"); 
    } 
} 

錯誤處理或許可以得到改善;也許整個編輯應該被中止。 (對文件進行強制性記錄鎖可以有一些寫入失敗,有的寫成功,但這是件不可思議的事。)

更新

fseek()呼叫立足基礎乘以新的文件位置帳號的大小爲struct clientData。這隻適用於如果你的記錄被排序,沒有數字被跳過,它開始於0和工程。 (您的示例輸入文件沒有排序;您甚至在「有效」記錄的中間有一個賬號爲0的記錄)。因此改用fseek(..., SEEK_CUR)代替。

+0

我希望我們學習了一個重要的課程,關於基於cut-n-paste的問題來拼湊問題:)我發現了另一個問題,很快發佈了更新。 – sarnold 2011-04-06 23:43:52

+0

@ user695752,有兩種方法:** 1 **繼續在你的'fseek(3)'中使用'(client.acctNum - 1)':(a)你必須確定你的記錄按照數字順序排序,在'1'處,你不能跳過輸入文件中的任何數字。 (b)你必須使用'SEEK_SET'。 ** 2 **或者,您可以使用不帶'client.acctNum'縮放的'SEEK_CUR',並且只備份剛讀過的_one_記錄。 – sarnold 2011-04-07 00:54:23

0

除了你的代碼中的@sarnold指出的邏輯錯誤,你從來沒有fflush()你的文件流或fclose()它。您所做的任何更新可能會保留在流緩衝區中,並在程序終止時丟失。

+0

如果程序運行完成,'exit(3)'將刷新並關閉所有'stdio(3)'流。但是好的建議不會少,因爲發生錯誤會阻止完全執行。 :) – sarnold 2011-04-06 23:46:55

0

這是代碼的相關部分目前顯示:

cfPtr = fopen("credit.dat", "rb+"); 
fread(&client, sizeof(struct clientData), 1, cfPtr); 
client.balance -= serviceCharge; 
fseek(cfPtr,(client.acctNum - 1) * sizeof(struct clientData), SEEK_CUR); 
fwrite(&client, sizeof(struct clientData), 1, cfPtr); 

我會認爲錯誤的函數的返回值是檢查「沒有必要」;它應該在那裏,但它會稍微複雜化。然而,我不相信自己在沒有錯誤檢查的情況下編寫一個程序 - 太多的東西可能會出錯我的口味(和痛苦的經歷)。

  • 您在文件中讀取偏移量爲0的記錄。然後,您更新餘額,根據相對於當前頭寸的客戶賬號尋找頭寸,然後寫回數據。

在另一次,我們可以討論您正在使用的二進制數據格式的智慧;暫時足夠了。

這裏的代碼,「作品」:

#include <stdio.h> 

struct clientData 
{ 
    int acctNum;   
    char lastName[15]; 
    char firstName[10]; 
    double balance;  
}; 

static void print_client(struct clientData *data) 
{ 
    printf("%04d %-15s %-15s %6.2f\n", data->acctNum, data->lastName, 
      data->firstName, data->balance); 
} 

int main(void) 
{ 
    FILE *cfPtr; 
    struct clientData client = {0, "Me", "Mine", 50.0}; 
    double serviceCharge = 5.0; 

    /* Initialize */ 
    cfPtr = fopen("credit.dat", "wb"); 
    fwrite(&client, sizeof(struct clientData), 1, cfPtr); 
    fclose(cfPtr); 

    /* Update */ 
    cfPtr = fopen("credit.dat", "rb+"); 
    fread(&client, sizeof(struct clientData), 1, cfPtr); 
    print_client(&client); 
    client.balance -= serviceCharge; 
    fseek(cfPtr, 0, SEEK_SET); 
    fwrite(&client, sizeof(struct clientData), 1, cfPtr); 
    fclose(cfPtr); 

    /* Validate */ 
    cfPtr = fopen("credit.dat", "rb"); 
    fread(&client, sizeof(struct clientData), 1, cfPtr); 
    fclose(cfPtr); 
    print_client(&client); 

    return 0; 
} 

這工作太...它不會跳過0的客戶端ID,因爲它不產生任何。但你可以解決這個問題。

#include <stdio.h> 

struct clientData 
{ 
    int acctNum;   
    char lastName[15]; 
    char firstName[10]; 
    double balance;  
}; 

static void print_client(const struct clientData *data) 
{ 
    printf("%04d %-15s %-15s %6.2f\n", data->acctNum, data->lastName, 
      data->firstName, data->balance); 
} 

/* Initialize file */ 
static void init_file(const char *filename) 
{ 
    FILE *cfPtr = fopen(filename, "wb"); 
    int i; 
    for (i = 1; i <= 30; i++) 
    { 
     struct clientData client = { i, "", "", i * 50.0}; 
     sprintf(client.lastName, "Me (%d)", i); 
     sprintf(client.firstName, "Mine (%d)", i); 
     fwrite(&client, sizeof(struct clientData), 1, cfPtr); 
    } 
    fclose(cfPtr); 
} 

static void print_file(const char *filename) 
{ 
    struct clientData client; 
    FILE *cfPtr = fopen(filename, "rb"); 
    while (fread(&client, sizeof(struct clientData), 1, cfPtr) == 1) 
     print_client(&client); 
    fclose(cfPtr); 
} 

int main(void) 
{ 
    FILE *cfPtr; 
    double serviceCharge = 5.0; 
    const char *filename = "credit.dat"; 
    struct clientData client; 

    init_file(filename); 
    printf("Post-initialization: %s\n", filename); 
    print_file(filename); 

    /* Update */ 
    cfPtr = fopen(filename, "rb+"); 
    while (fread(&client, sizeof(struct clientData), 1, cfPtr) == 1) 
    { 
     client.balance -= serviceCharge; 
     fseek(cfPtr, -1 * (long)sizeof(struct clientData), SEEK_CUR); 
     fwrite(&client, sizeof(struct clientData), 1, cfPtr); 
     fseek(cfPtr, 0, SEEK_CUR); 
    } 
    fclose(cfPtr); 

    printf("Post-update: %s\n", filename); 
    print_file(filename); 

    return 0; 
} 

又見評論...

+0

@ user695752:我們儘量不要給人喂太多東西。您可以使用循環編寫一個函數來創建一些記錄來代替我的代碼寫入的一條記錄 - 您必須先創建文件,然後才能更新它。您需要更改記錄編號以符合約定,記錄N的偏移量爲(N-1)×record_size。然後你可以尋找和閱讀,尋求和寫作來查找記錄。然後你可以用循環寫一個函數來轉儲文件的內容。也許你會提示在循環中更新記錄號。等等 – 2011-04-07 04:15:11

+0

@ user695752:根據C標準,您必須在每個開關之間在讀寫之間進行尋找;你只做一次尋求,而不是兩次。你也應該忘記'feof()';你通常不會在C中使用它 - 它表示帕斯卡背景。檢查'fread()'的結果,告訴你是否到達EOF(或遇到一些I/O錯誤)。這也意味着你不必寫兩次'fread()'語句 - 在主循環條件中代替'feof()'寫一次。 – 2011-04-07 04:47:42