ccsvfiletxtfile-manipulation

C - Read input from text file (int,string,int,int)


First of all sorry for any english error. So, I have this data in a .txt file

Codigo,Produto,StockMinimo,StockAtual
1,Couro,300,1000
2,Tecido,250,2000
...

I want to skip the first line and read only the integers to a struct

I'm using the bellow function to count the line of the file:

int nrLinhasFicheiro(char *filename){
    char ch;
    FILE *file;
    file = fopen(filename, "r");
    int linhas = 0;
    if (file != NULL) {
         for (ch = getc(file); ch != EOF; ch = getc(file)) 
        if (ch == '\n')  
            linhas = linhas + 1; 
    }
    fclose(file);
    return linhas - 1;
}

and this one to read the file to the struct "Stock" :

void carregarStock(Stock *stock, char *nomeFicheiro){
    int i = 0;
    int codigo; //stores "codigo" values
    char stringFake[40], *storeProduto; //variables to store first line and "Produto" string
    int minimo, quantidade;
    
    FILE *fp = fopen(nomeFicheiro, "r");
    
    int numeroLinhas = nrLinhasFicheiro(nomeFicheiro);
    
    if (fp != NULL){
       
        if(numeroLinhas >0){
            fscanf(fp, "%s",stringFake);//skip first line
            
            while(i< numeroLinhas){
                fscanf(fp, "%d,%s ,%d,%d", &codigo, storeProduto, &minimo, &quantidade);
                //fscanf(fp, "%d,%s %d,%d", &codigo, storeProduto, &minimo, &quantidade);  also tried this and don't work
                
                if(codigo==1){
                    stock->minStockCouro = minimo;
                    stock->couroStock = quantidade;
                }else if(codigo==2){
                    stock->minStockTecido = minimo;
                    stock->tecidoStock = quantidade;
                }else if(codigo==3){
                    stock->minStockBorracha = minimo;
                    stock->borrachaStock = quantidade;
                }else if(codigo==4){
                    stock->minStockCordoes = minimo;
                    stock->cordoesStock = quantidade;
                }else if(codigo==5){
                    stock->minStockPalmilhas = minimo;
                    stock->palmilhasStock = quantidade;
                }   
                i++;
            }
            
        }else{
            puts("Error");          
        }      
    }else{
        puts(MSG_ERRO_LER_FICHEIRO);
    }
    fclose(fp);
}

Function to print the struct:

void mostrarStock(Stock stock){
    puts("==============  Stock : =============\n");
    puts("Codigo | Produto | Stock Minimo | Stock Atual\n");
    printf("1\tCouro\t\t%d\t\t%d\n", stock.minStockCouro, stock.couroStock);
    printf("2\tTecido\t\t%d\t\t%d\n", stock.minStockTecido, stock.tecidoStock);
    printf("3\tBorracha\t%d\t\t%d\n", stock.minStockBorracha, stock.borrachaStock);
    printf("4\tCordões\t\t%d\t\t%d\n", stock.minStockCordoes, stock.cordoesStock);
    printf("5\tPalmilhas\t%d\t\t%d\n", stock.minStockPalmilhas, stock.palmilhasStock);

}   

This is how i call the function in main:

int main(int argc, char** argv) {
    
    char *nomeFicheiro1 = "tabela_stocks.txt";
    Stock stock;
    carregarStock(&stock, nomeFicheiro1);
    mostrarStock(stock);
    
    return (EXIT_SUCCESS);
}

The function to print the struct doesn't print. I guess the error is in

fscanf(fp, "%d,%s ,%d,%d", &codigo, storeProduto, &minimo, &quantidade);

What am I doing wrong?


Solution

  • You could create a helper function to read out the two integers from one line.

    bool get_two(FILE *stream, int* a, int* b) {
        return fscanf(stream, " %*[^,],%*[^,],%d,%d", a, b) == 2;
    }
    

    You then just need to call that 5 times.

    Example:

    #include <stdbool.h>
    #include <stdio.h>
    
    typedef struct {
        int couroStock;
        int minStockCouro;
        int tecidoStock;
        int minStockTecido;
        int borrachaStock;
        int minStockBorracha;
        int cordoesStock;
        int minStockCordoes;
        int palmilhasStock;
        int minStockPalmilhas;
    } Stock;
    
    bool get_two(FILE *stream, int* a, int* b) {
        return fscanf(stream, " %*[^,],%*[^,],%d,%d", a, b) == 2;
    }
    
    int main() {
        FILE *fp = fopen("tabela_stocks.txt", "r");
        if(fp) {
            Stock s;
            fscanf(fp, "%*[^\n]"); // skip first line
    
            if(get_two(fp, &s.couroStock, &s.minStockCouro) &&
               get_two(fp, &s.tecidoStock, &s.minStockTecido) &&
               get_two(fp, &s.borrachaStock, &s.minStockBorracha) &&
               get_two(fp, &s.cordoesStock, &s.minStockCordoes) &&
               get_two(fp, &s.palmilhasStock, &s.minStockPalmilhas))
            {
                printf("%d %d\n", s.couroStock, s.minStockCouro);
                printf("%d %d\n", s.tecidoStock, s.minStockTecido);
                printf("%d %d\n", s.borrachaStock, s.minStockBorracha);
                printf("%d %d\n", s.cordoesStock, s.minStockCordoes);
                printf("%d %d\n", s.couroStock, s.minStockCouro);
            }
            fclose(fp);
        }
    }
    

    If tabela_stocks.txt contains

    Codigo,Produto,StockMinimo,StockAtual
    1,Couro,300,1000
    2,Tecido,250,2000
    3,Borracha,200,500
    4,Cordões,500,2000
    5,Palmilhas,500,1000
    

    Then the output will be

    300 1000
    250 2000
    200 500
    500 2000
    300 1000
    

    Since there are so many similarities between the different stocks in your Stock struct, you could break it down into smaller pieces and make a struct only holding information about one stock:

    #include <stdbool.h>
    #include <stdio.h>
    
    typedef struct {
        int stock;
        int min_stock;
    } Stock;
    
    bool Stock_read(FILE *stream, Stock *s) {
        return fscanf(stream, " %*[^,],%*[^,],%d,%d", &s->stock, &s->min_stock) == 2;
    }
    
    void Stock_print(const Stock *s) {
        printf("%d %d\n", s->stock, s->min_stock);
    }
    

    With that you could create a Stocks type like this:

    typedef struct {
        Stock Couro;
        Stock Tecido;
        Stock Borracha;
        Stock Cordoes;
        Stock Palmilhas;
    } Stocks;
    
    bool Stocks_read(FILE *stream, Stocks *s) {
        return
            Stock_read(stream, &s->Couro) &&
            Stock_read(stream, &s->Tecido) &&
            Stock_read(stream, &s->Borracha) &&
            Stock_read(stream, &s->Cordoes) &&
            Stock_read(stream, &s->Palmilhas);
    }
    
    void Stocks_print(const Stocks *s) {
        Stock_print(&s->Couro);
        Stock_print(&s->Tecido);
        Stock_print(&s->Borracha);
        Stock_print(&s->Cordoes);
        Stock_print(&s->Palmilhas);
    }
    

    and reading and printing would become simpler:

    int main() {
        FILE *fp = fopen("tabela_stocks.txt", "r");
        if(fp) {
            Stocks s;
            fscanf(fp, "%*[^\n]"); // skip first line
    
            if(Stocks_read(fp, &s)) {
                Stocks_print(&s);
            }
            fclose(fp);
        }
    }
    

    Looking closer at the Stocks struct you may want to consider using an array instead:

    typedef struct {
        Stock stocks[5];
    } Stocks;
    

    which would also simplify the rest of the code since you could loop over that array to read and print each Stock.