Luca
Luca

Reputation: 70

Multiple fscanf statements in a loop. How do I get when EOF is reached?

I'm having problems while reading a file -again. Here's my code:

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

struct camion {
    int data[3];
    int numeropacchi;
    char *hubprovenienza[100];
    _Bool iseccezionale;
};

int main(void) {
    struct camion **listacamion = malloc(sizeof(struct camion * ));
    listacamion[0] = calloc(1, sizeof(struct camion));
    int numerocamion = 0;
    //importazione
    FILE *input = fopen("info.txt", "r");

    if (input != NULL) {
        char *temp = calloc(100, sizeof(char));

        for (int i = 0; true; i++) {
            //1, 2, 3
            fscanf(input, "%d - %d - %d",
                   &listacamion[i]->data[0],
                   &listacamion[i]->data[1],
                   &listacamion[i]->data[2]);
            printf("ORA %d-%d-%d\n",
                   listacamion[i]->data[0],
                   listacamion[i]->data[1],
                   listacamion[i]->data[2]);
            fscanf(input, "%d", &listacamion[i]->numeropacchi);
            printf("NUMERO PACCHI %d\n", listacamion[i]->numeropacchi);
            if (fscanf(input, "%s", listacamion[i]->hubprovenienza) == EOF) {
                listacamion[i]->iseccezionale = false;
                break;
            }
            printf("HUB PROVENIENZA %s\n", listacamion[i]->hubprovenienza);

            //4
            char eccezionale;
            long i_senoneccezionale = ftell(input);
            if (fscanf(input, "%s", temp) == EOF)
                break;
            else { //if not EOF alloc mem for the next truck
                listacamion = realloc(listacamion, sizeof(struct camion *) * (i + 1));
                listacamion[i + 1] = calloc(1, sizeof(struct camion))
                numerocamion++;
                printf("RIALLOCATA!\n");
            }
            if (temp[0] == 'E') //if 'E' is there, bool is true 
                listacamion[i]->iseccezionale = true;
            else {
                //otherwise go back to the saved pointer
                listacamion[i]->iseccezionale = false;
                fseek(input, i_senoneccezionale, SEEK_SET);
            }
            //set temp to zero;
            free(temp);
            temp = calloc(100, sizeof(char));
            //---
            printf("YES OR NO %d\n\n", listacamion[i]->iseccezionale);
        }
    }
    //---

    return 0;
}

Here's the input file I use:

22 - 03 - 2020
39
Bergamo
E
30 - 05 - 2021
90
Napoli
19 - 02 - 2022
132
Bari
21 - 03 - 2022
1721
Modena
E

I've got 3 or 4 lines per camion (truck) in my file, depending on whether the load is oversized or not (if it is, then an 'E' is put, otherwise the line in the file doesn't exist). So the end of the file could be detected either at the third fscanf or the fourth, but I'm not sure fscanf works this way... The issue is that the loop is correctly executed 3 times out of 4 (I've got the info of 4 trucks), then I get a segmentation fault, so I assumed that the problem might be an inadequate detection of the end of the file. I've tried other options - feof for example - but they weren't useful, and I can only find examples of a fscanf used as a condition, while my condition is just a true because I have different fscanfs...

Here's my output:

ORA 22-3-2020
NUMERO PACCHI 39
HUB PROVENIENZA Bergamo
RIALLOCATA!
YES OR NO 1

ORA 30-5-2021
NUMERO PACCHI 90
HUB PROVENIENZA Napoli
RIALLOCATA!
YES OR NO 0

ORA 19-2-2022
NUMERO PACCHI 132
HUB PROVENIENZA Bari
RIALLOCATA!

--------------------------------
Process exited after 0.4117 seconds with return value 3221226356

Upvotes: 1

Views: 66

Answers (3)

Luca
Luca

Reputation: 70

Okay, this morning I've tried again and now it's working. Thanks to your comments I've paid more attention to the fscanf return value; I've also realized that I could get an EOF from either the 1st line per truck or the 4th one, not 3rd and 4th. I didn't know whether to delete this question or what... so I've decided to post the code that worked for me just in case it might turn useful to some C beginners like me in the future because it contains a couple of things I wish I knew before. I'm also quite sure there's a faster way to do so, but who knows...

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

struct camion {
    int data[3];
    int numeropacchi;
    char* hubprovenienza[100];
    _Bool iseccezionale;
};

int main(void) {
    struct camion** listacamion=malloc(sizeof(struct camion*));
        listacamion[0]=calloc(1, sizeof(struct camion));
    int numerocamion=0;
    //importazione
    FILE* input=fopen("info.txt", "r");
    
    if(input!=NULL) {
        char* temp=calloc(100, sizeof(char));
        _Bool primavolta=true;

        for(int i=0; true; i++) {
            int fs1, fs4;
            int temp1, temp2, temp3;
        
            //1
            if((fs1=fscanf(input, "%d - %d - %d", &temp1, &temp2, &temp3) == EOF))
                break;
            else {
                numerocamion++;
            
                if(primavolta) {
                    primavolta=false;
                    listacamion[i]->data[0]=temp1;
                    listacamion[i]->data[1]=temp2;
                    listacamion[i]->data[2]=temp3;
                }
                else {
                    //EVENTUALE RIALLOCAZIONE
                    listacamion=realloc(listacamion, sizeof(struct camion*)*(i+1));
                    listacamion[i]=calloc(1, sizeof(struct camion));
                    //---
                
                    listacamion[i]->data[0]=temp1;
                    listacamion[i]->data[1]=temp2;
                    listacamion[i]->data[2]=temp3;
                }
            }
            //2
            fscanf(input, "%d", &listacamion[i]->numeropacchi);

            //3
            fscanf(input, "%s", listacamion[i]->hubprovenienza);
        
            //4
            char eccezionale;
                long i_senoneccezionale=ftell(input);
        
            if((fs4=fscanf(input, "%s", temp)) == EOF)  {//se è la fine del file...
                listacamion[i]->iseccezionale=false;
                free(temp);
                break;
            }
            else {
                if(temp[0]=='E') //se esiste il carattere 'E' - e quindi la quarta riga ESISTE - assegna il valore positivo alla booleana 
                    listacamion[i]->iseccezionale=true;
                else { /*se invece NON esiste il carattere 'E', ripristina il puntatore al file alla stessa riga che avrebbe dovuto contenere la 'E',
                     così che questo non vada perso per la prossima chiamata a scanf per la prima riga di ciascun camion, quella contenente la data*/
                    listacamion[i]->iseccezionale=false;
                    fseek(input, i_senoneccezionale, SEEK_SET);
                }
            }
        
            //ripristina memoria per temp;
            free(temp);
            temp=calloc(100, sizeof(char));
            //---
        }
    }
    //---

    //output
    for(int i=0; i<numerocamion; i++) {
        printf("%d-%d-%d %d %s %d\n", listacamion[i]->data[0], listacamion[i]->data[1], listacamion[i]->data[2], listacamion[i]->numeropacchi, listacamion[i]->hubprovenienza, listacamion[i]->iseccezionale);
    }

    return 0;
}

Upvotes: 0

William Pursell
William Pursell

Reputation: 212684

Check the return value of every call to scanf. You can refactor your code to make it simpler. Try something like:

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

struct camion {
    int data[3];
    int numeropacchi;
    char hubprovenienza[100];
    _Bool iseccezionale;
};

int
get_record(FILE *fp, struct camion *c)
{
    /* Read up to one record from the file.  Return 1 on success. */
    if( 3 != fscanf(fp, "%d -%d -%d", c->data, c->data + 1, c->data + 2) ){
        return 0;
    }
    if( 1 != fscanf(fp, "%d", &c->numeropacchi) ){
        return 0;
    }
    if( 1 != fscanf(fp, "%99s", c->hubprovenienza) ){
        return 0;
    }
    int k;
    while( ( k = fgetc(fp)) != EOF && isspace(k) ){
        ;
    }
    if( k == 'E' ){
        c->iseccezionale = 1;
    } else {
        c->iseccezionale = 0;
        ungetc(k, fp);
    }
    return 1;
}


int
main(int argc, char **argv)
{
    struct camion c;
    while( get_record(stdin, &c) ){
        printf(
            "%s is %soversized\n",
             c.hubprovenienza,
             c.iseccezionale ? "" : "not "
        );
    }
    return 0;
}

Upvotes: 1

chqrlie
chqrlie

Reputation: 145317

You should test the return value of every call to fscanf(): fscanf returns the number of successful conversions. A return value different from the number of conversions requested is an indication of invalid or missing input. Both must be handled to avoid undefined behavior.

fscanf(input, "%s", temp) is risky: a word longer than 99 bytes will cause a buffer overflow. You should write fscanf(input, "%99s", temp)

Here is a modified version using an array of structures instead of an array of pointers:

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

struct camion {
    int data[3];
    int numeropacchi;
    char *hubprovenienza[100];
    _Bool iseccezionale;
};

int main() {
    struct camion *listacamion = NULL;
    struct camion cam;
    char ebuf[2];
    int numerocamion = 0;
    //importazione
    FILE *input = fopen("info.txt", "r");
    if (input != NULL) {
        while (fscanf(input, "%d - %d - %d\n%d\n%99s",
                      &cam.data[0], &cam.data[1], &cam.data[2],
                      &cam.numeropacchi,
                      cam.hubprovenienza) == 5) {
            cam.iseccezionale = false;
            if (fscanf(input, " %1[E]", ebuf) == 1)
                cam.iseccezionale = true;

            printf("ORA %d-%d-%d\n"
                   "NUMERO PACCHI %d\n"
                   "HUB PROVENIENZA %s\n"
                   "YES OR NO %d\n\n",
                   cam.data[0], cam.data[1], cam.data[2]);
                   cam.numeropacchi,
                   cam.hubprovenienza,
                   cam.iseccezionale);

            // if new truck, append to array
            listacamion = realloc(listacamion, sizeof(*listacamion) * (numerocamion + 1));
            listacamion[numerocamion] = cam;
            numerocamion++;
            printf("RIALLOCATA!\n");
        }
    }
    return 0;
}

Upvotes: 1

Related Questions