Whats wrong with fread in this program?

2019-03-01 11:30发布

问题:

I'm intermediate student of C. I'm trying to make a bank management program but first I need to make a login program, so I created one of the following. As I've recently learned about file I/O in C and don't know much about fread and fwrite. I have a file (data.txt) which format if as following.

user1 1124

user2 3215

user3 5431

In the following program I've asked user to input user name and pin(4-digit password) and copy file data into a structure then compare these two for verifying information.

What is wrong with my program and how to make fread work properly. And is the formating in data.txt file all right or should I change it.

Thanks in advance...

#include<stdio.h>
#include<ctype.h>
#include<string.h>

struct user_account    {
    char u_name[30];
    int u_pin;
} log_in;



    int login()
{
    int start;
    int i, n;
    int t_pin[4];       // TEMPORARY INT PIN for storing pin inputed by user
    char t_name[30];    //  TEMPORARY STRING for storing name inputed by user

    FILE *fp;
    fp = fopen("data.txt","rb");        // Opening record file

    if(fp == NULL)
    {
    puts("Unable to open file!");
    return 1;
    }

    start :  {
        printf("User Name : ");
        scanf("%s",&t_name);
        printf("Pin Code  : ");

        for(i = 0; i < 4; i++)  {       // This loop is for hiding input pin
            n = getch();

            if(isdigit(n))  {
                t_pin[i] = n;
                printf("*");    }
            else    {
                printf("\b");
                i--;
            }
        }

        fread(&log_in,sizeof(log_in),1,fp);

        // Comparing user name and pin with info in the structure copied from the file

        if(strcmp(log_in.u_name, t_name) == 0 && log_in.u_pin == t_pin)
            puts("Login successful! Welcome User");
        else    {
            printf("\nIncorrect Information!\n");
            printf("Press any key to log in again...");
            getch();
            system("cls");
            goto start; }
        }
    }

    int main()
    {
    int login();
        return 0;
    }

回答1:

The problem is that you have an ASCII/text file but you are trying to use fread to read directly into a structure; this requires a binary formatted file. fread cannot do format conversion. Use fscanf instead:

fscanf(fp, "%s %d", &log_in.u_name, &log_in.u_pin);


回答2:

Problems that I see:

  1. The following line is incorrect.

    scanf("%s",&t_name);
    

    You need to use:

    scanf("%29s",t_name);
    
  2. fread is not the right solution given the file format. fread works when the file is in binary format.

    Given the format of your input file, you need to use:

     scanf("%29s %d", log_in.uname, &log_in.u_pin); 
    
  3. Comparing the pins using log_in.u_pin == t_pin should produce a compiler error. The type of log_in.u_pin is int while the type of t_pin is int [4]. You will have to change your strategy for getting the integer value from t_pin.

    You can use something like:

    int pin = 0; 
    for (i = 0; i < 4; ++i )
    {
       pin = 10*pin + t_pin[i];
    }
    

    However, for that to work, you have store the proper integer values in t_pin. When the character you read is '1', you have to store 1. Either that, or you will have to change the above for loop to:

       pin = 10*pin + t_pin[i]-'0';
    
  4. The way are using goto start to loop in your function is not correct. You haven't read all the user ID and pins to from the input file and compared them. The way you have it, you will end up doing:

    • Read the user name and pin
    • Check against the first entry in the file. If they don't match...

    • Read the user name and pin again.

    • Check against the next entry in the file. If they don't match...

    etc.

    You want to:

    • Read the user name and pin

    • Check against the first entry in the file. If they don't match...

    • Check against the next entry in the file. If they don't match...
    • Check against the next entry in the file. If they don't match...

    etc.



回答3:

a) fread is used to read fixed sized (in bytes) records. While that is an option, you might find that fscanf is more suited to your use case.

b) goto has its uses, arguably, but in your particular case a while loop is far more appropriate as it make the intention clearer and is less susceptible to misuse.

c) The crux of your issue is reading the file. You only read in and check against a single record from your file per pass and once all records are read you will be getting EOF rather than additional records.

You have a few options. You might read the entire user table into a list prior to your login loop. When a user logs in you must iterate through the list until a match or end of list is found. Alternatively you might loop through the entire file on each user attempt looking for a match and seek to the back to the beginning of the file when you are done. In either case you must check for and handle read errors.

d) Finally when a non-digit is entered you will probably find that backspace isn't what you had in mind. Also you might consider allowing the user to press backspace while entering the pin.