简体   繁体   中英

reading from a file in c in a while loop

I'm trying to read patient information from a file, insert the data from the file into fields of a structure, and insert the node into a linked list until I reach an EOF. The problem i'm having is trying to terminate my while loop when I reach the EOF. I used a fscanf to read each line of the file, and another one to check for end of file. Put having 2 fscanf's causes a new line everytime and skips half of my data. What is a better way to check for an EOF in a while loop? The information in my text document is formatted as such. Except they're aren't newlines in between each patient. I just did that on this post for readability.

Sophia Jackson 1234 141.0 1.1

Emma Aiden 5432 142.0 1.2

Olivia Lucas 5685 143.0 1.3

Ava Liam 5672 144.0 1.4

Mia Noah 3467 145.0 1.5

Isabella Ethan 8654 146.0 1.6

Riley Mason 2567 147.0 1.7

Aria Caden 6794 148.0 1.8

Zoe Oliver 3467 149.0 1.9

fp = fopen("info.txt","r");

if(fp == NULL){
    printf("ERROR File Doesn't exist\n");
}


while(1){

    newptr = (node*)malloc(sizeof(node));
    newptr->back = NULL;
    newptr->next = NULL;
    newptr->phgnum = 0;
    newptr->pid = 0;
    newptr->pwt = 0;
    fscanf(fp,"%s %s %i %f %f",newptr->pfn,newptr->pln,&newptr->pid,&newptr->pwt,&newptr->phgnum);
    printf("First Name: %s\n",newptr->pfn);
    printf("Last Name: %s\n",newptr->pln);
    printf("PID: %i\n",newptr->pid);
    printf("Weight: %f\n",newptr->pwt);
    printf("HG1AC: %f\n",newptr->phgnum);

    (insert node into linked list)

    // test for EOF        
    if( 5!= fscanf(fp,"%s %s %i %f %f",newptr->pfn,newptr->pln,&newptr->pid,&newptr->pwt,&newptr->phgnum)) 
        break;

When you are reading formatted data, you want to control the loop based upon the successful read of a unit of data (a line here). That insures that you only attempt to process information after you have validated the read. While it is generally a better idea to use fgets and then call sscanf , when reading from a formatted file, it end up being equivalent to your fscanf call. The only distinction being that you lose the ability to independently validate the read and the parse. (whether that is important is situation dependent)

For your read, you are better served by reading values into a temporary struct before allocating for the node. No allocation is needed prior to the read allowing you to validate your read before making the call to malloc . There are varying ways to do this depending on your needs, but a general approach would be:

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

#define MAXNAME 16  /* size as appropriate - or dynamically allocate */

typedef struct {    /* declare struct as required */
    char pfn[MAXNAME],
         pln[MAXNAME];
    float pwt,
          phgnum;
    int pid;
} node_t;

int main (int argc, char **argv) {

    size_t npt = 0;                 /* number of patients */
    node_t tmp = { .pfn = "" };     /* temp struct for read */
    /* read from filename given as 1st argument (or stdin by defaut) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    /* while there is a good read of patient data */
    while (fscanf (fp, "%15s %15s %d %f %f", tmp.pfn, tmp.pln, &tmp.pid,
            &tmp.pwt, &tmp.phgnum) == 5) {
        node_t *newptr = malloc (sizeof *newptr);   /* allocate node */

        if (!newptr) {  /* validate allocation */
            perror ("malloc-newptr");
            break;
        }

        *newptr = tmp;  /* assign tmp data to node */
        /* add to linked list -- here */

        printf ("%-16s %-16s %5d %6.2f %6.2f\n", newptr->pfn, newptr->pln,
                newptr->pid, newptr->pwt, newptr->phgnum);

        npt++;  /* increment patient count */
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */
                                    /* don't forget to free list */
    return 0;
}

(note: the data is simply output above, you simply need to add it to your list and remove the output (or wrap it in #ifdef DEBUG ... #endif to make it conditional on whether DEBUG was defined))

Example Input File

$cat dat/patients.txt
Sophia Jackson 1234 141.0 1.1
Emma Aiden 5432 142.0 1.2
Olivia Lucas 5685 143.0 1.3
Ava Liam 5672 144.0 1.4
Mia Noah 3467 145.0 1.5
Isabella Ethan 8654 146.0 1.6
Riley Mason 2567 147.0 1.7
Aria Caden 6794 148.0 1.8
Zoe Oliver 3467 149.0 1.9

Example Use/Output

$ ./bin/patient_read <dat/patients.txt
Sophia           Jackson           1234 141.00   1.10
Emma             Aiden             5432 142.00   1.20
Olivia           Lucas             5685 143.00   1.30
Ava              Liam              5672 144.00   1.40
Mia              Noah              3467 145.00   1.50
Isabella         Ethan             8654 146.00   1.60
Riley            Mason             2567 147.00   1.70
Aria             Caden             6794 148.00   1.80
Zoe              Oliver            3467 149.00   1.90

Look things over and let me know if you have any questions.

The return value of fscanf() is EOF upon end of file, or 0 in case of early matching failure. Otherwise, the number of successfully matched input items is returned. As such, you can make this the test condition for your while loop itself:

while (fscanf(fp, "%s %s %i %f %f", ...) == 5) {
    // ...
}

For example, consider the following minimal program:

#include <stdio.h>

int main(void) {
    char fname[50], lname[50];
    int id;
    float weight, hgnum;

    FILE *fp = fopen("info.txt", "r");
    if (fp == NULL) {
        perror("Error: Failed to open file.");
        return 1;
    }

    while (fscanf(fp, "%49s %49s %i %f %f", fname, lname, &id, &weight, &hgnum) == 5) {
        printf("First Name: %s\n", fname);
        printf("Last Name: %s\n", lname);
        printf("PID: %i\n", id);
        printf("Weight: %.1f\n", weight);
        printf("HG1AC: %.1f\n\n", hgnum);
    }

    return 0;
}

This will print your text file sample as follows:

First Name: Sophia
Last Name: Jackson
PID: 1234
Weight: 141.0
HG1AC: 1.1

First Name: Emma
Last Name: Aiden
PID: 5432
Weight: 142.0
HG1AC: 1.2

First Name: Olivia
Last Name: Lucas
PID: 5685
Weight: 143.0
HG1AC: 1.3

First Name: Ava
Last Name: Liam
PID: 5672
Weight: 144.0
HG1AC: 1.4

First Name: Mia
Last Name: Noah
PID: 3467
Weight: 145.0
HG1AC: 1.5

First Name: Isabella
Last Name: Ethan
PID: 8654
Weight: 146.0
HG1AC: 1.6

First Name: Riley
Last Name: Mason
PID: 2567
Weight: 147.0
HG1AC: 1.7

First Name: Aria
Last Name: Caden
PID: 6794
Weight: 148.0
HG1AC: 1.8

First Name: Zoe
Last Name: Oliver
PID: 3467
Weight: 149.0
HG1AC: 1.9

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM