2013-12-22 4 views
2

계정 번호, 클라이언트 이름 및 은행 잔액의 세 가지 요소가있는 은행 클라이언트 (BankAccount) 용 구조체가있는 프로그램을 작성하고 있습니다. 이미 레코드가있는 파일이 있는데 균형을 수정할 수있는 기능이 있습니다. 그러나, 나는 그렇게 할 수 없다. 균형은 동일하게 유지됩니다. 내 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

파일에는 레코드 (또는 다른 구조)가 없습니다.그것들은 바이트 스트림 (또는 시퀀스) 일뿐입니다. [GDBM] (http://www.gnu.org.ua/software/gdbm/) 또는 [SQlite] (http://www.sqlite.org/)를 고려 했습니까? 코드가 실행되는 운영 체제는 무엇입니까? –

+0

@BasileStarynkevitch 저는 CS를 공부하는 신입생입니다. 우리는 C로 FileIO를 배우기 시작 했으므로 GDBM이나 SQlite에 대해서는 전혀 몰랐습니다. (데이터베이스와 관련이 있다고 생각 하지만요?) Windows 8을 실행 중입니다. – MMA

+0

@mbratch 예, 저는 계정 정보와 함께 "*** Account found ***"를 참조하십시오. 또한 새 잔액에 대한 입력을 할 수 있지만 레코드를 다시 인쇄 할 때 잔액을 겹쳐 쓰지 않습니다 (다른 기능이 있음). 그리고 이진 파일에 대해 유감스럽게 생각합니다. 이해가 안됩니다. "바이너리로 읽기/쓰기"란 정확히 무엇을 의미합니까? – MMA

답변

1

문제는 fseek() 호출 될 수있다 :

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

sizeof()의 반환 값은 부호없는 형식입니다; 그것의 부정은 아주 큰 숫자가 될 것입니다. 기술적으로 올바르지 않습니다 (fseek()은 이 아닌 long이됩니다). 그러나, sizeof(size_t) == sizeof(long)이라면, 그걸 가지고 도망 갈 수 있습니다.

문제의 또 다른 측면은 반환 여부 (레코드를 찾았는지 여부)에 관계없이 파일을 닫지 않는 것일 수 있습니다. 그것은 확실히 메모리 누수입니다. 데이터가 디스크에 기록되는 방식에도 영향을 줄 수 있습니다. 이것은 아마도 문제의 근본 원인 일 것입니다. 파일을 열어 변경 내용을 볼 수있는 다른 기능이 있지만 파일이 닫히지 않아 데이터가 디스크에 기록되지 않았습니다. 참고 : 이제 소스를 사용할 수 있으므로 이것이 문제의 원인입니다.

데이터 구조를 표시하지 않으므로 balance 멤버의 경우 floatdouble 유형이 일치하지 않을 수 있습니다. 마찬가지로 float은 계좌 잔액에 대해 부적절한 유형일 수 있습니다 (예 : 약 100,000.00 달러를 가장 가까운 센트로 신뢰 할 수는 없습니다. 예를 들어, 199999.99를 입력하면 199999.98로 표시됩니다).

참고 : LinuxWindows의 최신 버전에서는 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()의 변환에 대해조차 한 Witter을한다 옵션 :

실행
$ 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를 확인하는 나의 방식이 잘못되었음을 알지 못했습니다. 감사합니다. scanf() 또는 gets()를 실행할 때마다 fflush (stdin) 명령을 사용합니다. 그렇지 않으면 문제가 발생합니다. 내가 여기서해야 할 일보다 자주 여기에서 사용 했으므로 저 또한 경고 해 주셔서 감사드립니다. – MMA

+0

반갑습니다. 세 가지 기능 각각에서 파일 이름을 반복하는 것과 같은 사소한 문제가 있습니다. 변수 또는 상수에서 한 번 정의되어야하며 변수는 다양한 함수로 전달됩니다. 내 코드는'write()'와'read()'를 사용한다; POSIX에 정의 된 함수 이름이기도합니다. 작성된 코드는 안전하며 POSIX-ish 시스템에서 작동하지만 실제로 다른 이름을 사용해야합니다. 나는 당신의 코드를'scanf()'호출을 검사하는 에러로 업그레이드하지 않았다; 모든 입력 (및 열린) 작업을 확인해야합니다. 프롬프트없이'getch() '를 사용하지 않도록 조심하십시오. –

관련 문제