简体   繁体   中英

Read Text File with blank lines

The Text File was writable with 1 blank line after the struct.
Is similar to:

1  111 1 Peter

22 22  2 John Lays

3  3   3 Anne Belgs

The struct is:

struct estruturaCarro {
    int id, potencia, avariado;
    char name[11]; 
} carro;

I have write the data to the text File:

fprintf(fp, "%-2d %-3d %-1d %-10s \n\n", carro.id, carro.potencia, carro.avariado, carro.name);

I read the file data this way, but I'm sure it's not the best way:

while(true){
    int result = fscanf(fp, "%d %d %d %10[^\n]", &carro.id, &carro.potencia, &carro.avariado, &carro.name);
    if(result==4){    
            printf("%-2d %-3d %-1d % -s\n", carro.id, carro.potencia, carro.avariado, carro.name);
    }else break;
}

How can I read the text file, saving the data struct, without reading the blank lines?

If I want to validate the values ​​read (ID = xx, potencia = xxx, avariado = x, name = 10 characters), is it better, before or after filling the array of struct?
The format of each line of the file is:
xx xxx x aaaaaaaaaa (x = digits, a = characters)
For example, if one of the lines is
9 123 4 Error 1st value
stop reading the file (informing the user), because the line should be:
9 123 4 Error 1st value (one space was missing after 9) - or because NAME has more than 10 characters.

You don't show how true is set in while(true) , but it must be based on the return of fscanf in the way you are approaching it. You must test three cases:

  1. fscanf return is EOF , read done, true should be set to FALSE to break loop;
  2. fscanf return less than 4 , matching or input failure, no guarantee this occurred due to an empty line; and
  3. fscanf return is equal to 4 , good input.

Unless you are checking all three, you cannot cover all cases for fscanf and further, if there is any additional or extraneous character in your input file, your formatted read will fail.

That is why a better option is to read each line into a buffer with fgets and then parse the information you need from the buffer itself with sscanf . This provides the benefit of allowing separate validation of (1) the read; and (2) the parse of information. Further, since you are consuming a line-at-a-time, there is no uncertainty on what remains in the input stream and a failure in parsing any one line does not prevent a successful read of the remainder.

A short implementation with fgets() and sscanf() reading each struct into an array-of-struct, you can do something similar to the following:

#include <stdio.h>

#define MAXN   11   /* if you need a constant, #define one (or more) */
#define MAXC 1024   /*         (don't skimp on buffer size)          */

typedef struct {    /* use a simple typedef */
    int id, potencia, avariado;
    char name[MAXN]; 
} carro_t;

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

    char buf[MAXC];                         /* buffer to read line */
    carro_t carro[MAXN] = {{ .id = 0 }};    /* array of struct  */
    size_t ndx = 0;                         /* array index      */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (ndx < MAXN && fgets (buf, MAXC, fp)) {   /* read each line */
        carro_t tmp = { .id = 0 };                  /* temp struct */
        if (*buf == '\n')           /* if line empty */
            continue;               /* get next line */
        if (sscanf (buf, "%d %d %d %10[^\n]",   /* separate/validate */
                    &tmp.id, &tmp.potencia, &tmp.avariado, tmp.name) == 4)
            carro[ndx++] = tmp;     /* add to array of struct */
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    for (size_t i = 0; i < ndx; i++)
        printf ("%3d  %3d  %3d  %s\n",
            carro[i].id, carro[i].potencia, carro[i].avariado, carro[i].name);

    return 0;
}

( note: the filename is provide as the first argument to the program, or if no filename is provided, read from stdin by default)

While with your particular data file, there is no reason you cannot use fscanf , it is fragile and one character too many (like "Anne Belgss" ) will cause it to break. A fscanf implementation removing the true and simply looping as you have could be:

    for (;;) {
        carro_t tmp = { .id = 0 };
        if (fscanf (fp, "%d %d %d %10[^\n]",    /* separate/validate */
                    &tmp.id, &tmp.potencia, &tmp.avariado, tmp.name) == 4)
            carro[ndx++] = tmp;     /* add to array of struct */
        else
            break;
    }

Either way, you will produce the same output with "this" input file, eg

Example Use/Output

$ ./bin/readwblanks ~/tmpd/file
  1  111    1  Peter
 22   22    2  John Lays
  3    3    3  Anne Belgs

just use simple reading way is ok,like this.

    while(fscanf(fp,"%d %d %d %s",&carro.id,&carro.potencia,&carro.avariado,carro.name)!=EOF)
    printf("%d %d %d %s\n",carro.id,carro.potencia,carro.avariado,carro.name);

result is like this enter image description here

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