2013-12-22 4 views
2

Я пишу программу, которая имеет структуру для банковских клиентов (BankAccount), имеющих 3 элемента - номер счета, имя клиента и банковский баланс. У меня есть файл с уже записанными записями, и у меня есть функция для изменения баланса. Однако я не могу этого сделать. Баланс остается прежним. Я думаю, что мой fseek() ошибочен, но я не уверен, как/почему. Я использовал fflush (stdin) в некоторых местах, где это может не потребоваться, но я не думаю, что это связано с проблемой. Я прокомментировал, чтобы вы знали свою логику, на всякий случай в моих понятиях есть недоразумение. Вот код:C Перезаписать существующую запись в файле

void modify(){ 

    int account_number; 
    FILE *ptr; 
    BankAccount account; 

    ptr = fopen("account.txt", "r+"); 
    printf("Enter account number: "); 
    fflush(stdin); 
    scanf("%d", &account_number); 

    while (!feof(ptr)) // To search the whole "account.txt" file. 
    { 
     fread(&account, sizeof(BankAccount), 1, ptr); //brings record into memory 
     if (account.account_number == account_number){ // if record's account number is same as account number entered by user above 
      printf("***Account found***\n\nAccount number: %d\nAccount name: %s\nAccount balance: %.2f\n", account.account_number, account.name, account.balance); 
      printf("\nEnter new balance: "); 
      fflush(stdin); 
      scanf("%f", &account.balance); // rewrites account's balance in memory 
      fseek(ptr, -sizeof(BankAccount), SEEK_CUR); //pointer seeked to the beginning of the record to overwrite it with the one in memory 
      fwrite(&account,sizeof(BankAccount), 1, ptr); // record overwritten 
      return; 

     } 
    } 


    printf("Account not found\n"); 
    fflush(stdin); 
    getch(); 
} 

Вот весь .cpp файл моего проекта в случае, если вы хотите, чтобы запустить его: Source code. Я был бы признателен за некоторые рекомендации. Заранее спасибо.

+0

Файлы C не имеют записей (или некоторых других структур).Это всего лишь поток (или последовательность) байтов. Вы считали [GDBM] (http://www.gnu.org.ua/software/gdbm/) или [SQlite] (http://www.sqlite.org/)? На какой операционной системе работает ваш код? –

+0

@BasileStarynkevitch Я новичок, изучающий CS. Мы только начали изучать FileIO на C, поэтому я понятия не имею о GDBM или SQlite (хотя я думаю, что они что-то делают с базами данных и т. Д.). Запуск Windows 8. – MMA

+0

@mbratch Да, я могу см. «*** Учетная запись ***» вместе с данными учетной записи. Я также могу принимать входные данные для нового баланса, но баланс не перезаписывается, когда я снова печатаю запись (для этого есть другая функция). И, я сожалею о двоичном материале, не понимаю этого. Что именно вы подразумеваете под «чтением/письмом в двоичном формате»? – MMA

ответ

1

Проблема может быть fseek() вызова:

fseek(ptr, -sizeof(BankAccount), SEEK_CUR); 

Возвращаемое значение из sizeof() является тип без знака; отрицание этого будет очень большим числом. Технически это неверно (fseek() должен получить long, а не size_t). Однако, если sizeof(size_t) == sizeof(long), вы уйдете с ним (это работает для меня).

Другой аспект проблемы может заключаться в том, что вы не закрываете файл перед возвратом (независимо от того, находите ли вы запись или нет). Это, безусловно, утечка памяти; это может также повлиять на то, как данные записываются на диск. Вероятно, это основная причина ваших проблем. У вас есть другая функция, которая открывает файл для чтения данных, чтобы увидеть изменения, но поскольку файл не закрыт, данные не записываются на диск. Примечание: Поскольку источник теперь доступен, это является причиной проблем.

Поскольку вы не показываете нам структуру данных, другая проблема может быть несоответствием типа float vs double для члена balance; В равной степени единственная проблема может заключаться в том, что float является неподходящим типом для остатков на счетах (он не может достоверно представлять балансы выше примерно 100 000,00 долларов США, например, до ближайшего центрирования - например, ввод 199999.99 отображается как 199999,98).

Примечание: В современных версиях Linux и на Windows, fflush(stdin) - определенная операция (и определенное поведение является разумным и полезным). Согласно стандарту C и POSIX, он дает неопределенное поведение. Будьте осторожны с его использованием - имейте в виду, что это не переносная операция.

превращена в SSCCE (Short, Self-Contained, Correct Example), очень незначительная модификация кода (добавление fclose()) работает для меня:

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

typedef struct BankAccount 
{ 
    int account_number; 
    char name[20]; 
    float balance; 
} BankAccount; 

static void modify(void) 
{ 
    int account_number; 
    FILE *ptr; 
    BankAccount account; 

    ptr = fopen("account.txt", "r+"); 
    printf("Enter account number: "); 
    fflush(stdin); 
    scanf("%d", &account_number); 

    while (!feof(ptr)) 
    { 
     fread(&account, sizeof(BankAccount), 1, ptr); 
     printf("***Account read***(%d: %s: %.2f)\n", 
       account.account_number, account.name, account.balance); 
     if (account.account_number == account_number) 
     { 
      printf("***Account found***\n\nAccount number: %d\nAccount name: %s\nAccount balance: %.2f\n", account.account_number, account.name, account.balance); 
      printf("\nEnter new balance: "); 
      fflush(stdin); 
      scanf("%f", &account.balance); 
      fseek(ptr, -sizeof(BankAccount), SEEK_CUR); 
      fwrite(&account, sizeof(BankAccount), 1, ptr); 
      fclose(ptr); 
      return; 
     } 
    } 

    printf("Account not found\n"); 
    fflush(stdin); 
    fclose(ptr); 
} 

static void write(void) 
{ 
    FILE *fp = fopen("account.txt", "w"); 
    if (fp == 0) 
    { 
     fprintf(stderr, "Create file failed\n"); 
     exit(1); 
    } 
    static const BankAccount data[] = 
    { 
     { 1, "His", 20.00 }, 
     { 2, "Hers", 2000.00 }, 
     { 3, "Theirs", 1.00 }, 
    }; 
    if (fwrite(data, sizeof(data), 1, fp) != 1) 
    { 
     fprintf(stderr, "Write file failed\n"); 
     exit(1); 
    } 
    fclose(fp); 
} 

static void read(void) 
{ 
    FILE *fp = fopen("account.txt", "r"); 
    if (fp == 0) 
    { 
     fprintf(stderr, "Open file failed\n"); 
     exit(1); 
    } 
    BankAccount ac; 
    while (fread(&ac, sizeof(ac), 1, fp) == 1) 
    { 
     printf("A/C: %4d %-20s %8.2f\n", ac.account_number, ac.name, ac.balance); 
    } 
    fclose(fp); 
} 

int main(void) 
{ 
    write(); 
    read(); 
    modify(); 
    read(); 
    return 0; 
} 

компилятор даже не Виттер о конверсии на fseek() с компилятором варианты:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \ 
     -Wold-style-definition -Werror ba.c -o ba 

При запуске он показывает:

A/C: 1 His      20.00 
A/C: 2 Hers     2000.00 
A/C: 3 Theirs     1.00 
Enter account number: 2 
***Account read***(1: His: 20.00) 
***Account read***(2: Hers: 2000.00) 
***Account found*** 

Account number: 2 
Account name: Hers 
Account balance: 2000.00 

Enter new balance: 4000 
A/C: 1 His      20.00 
A/C: 2 Hers     4000.00 
A/C: 3 Theirs     1.00 

Обратите внимание на правильную форму для проверки того, достигли ли вы конца файла в функции read(). Если вы звоните feof(), вы делаете это неправильно 99,9% времени.

+0

Спасибо за отладку! Я полностью забыл fclose(), хотя я использовал его для других функций. Я понял, насколько это важно. Кроме того, я не знал, что мой способ проверить EOF был неправильным, спасибо за это. Я использую команду fflush (stdin) всякий раз, когда я сканирую f() или gets(), в противном случае это вызывает проблемы. Я использовал его здесь чаще, чем следовало бы, поэтому спасибо, что предупредил меня об этом. – MMA

+0

Добро пожаловать. Обратите внимание, что есть некоторые другие незначительные проблемы, такие как повторение имени файла в каждой из трех функций; который должен быть определен один раз в переменной или константе, а переменная передана различным функциям. Мой код использует 'write()' и 'read()'; они также являются именами функций, определяемыми POSIX. Код в письменном виде безопасен (и работает в системах POSIX-ish), но должен действительно использовать другие имена. Я не обновлял ваш код, чтобы проверить ошибку 'scanf()' call; он должен быть проверен - все входные (и открытые) операции должны быть проверены. Будьте осторожны при использовании 'getch()' без подсказки. –

Смежные вопросы