1

Hi I am trying to read a comma seprated file into a struct in c. the problem is they have spaces in between the same sentence so fscanf doesnt work well.

Text file:

9780136019701,An Introduction to Organic Chemistry,Timberlake Karen,10,3.54,12-2008
9781506304212,Mathematics for Social Scientists,Kropko Jonathan,7,4.73,12-2015
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Date
{
    int year;
    int month;
} Date;

typedef struct Book
{
    long long int isbn;
    char title[100];
    char author[100];
    int quantity;
    int price;
    Date date;
} Book;

int main() {
    Book* Books;
    int n_books=0;
    FILE *ptr = fopen("Books.txt", "r");

    if(ptr!=NULL) {
        Books = (Book*)malloc(sizeof(Book));

        while(!feof(ptr)) {

            fscanf(ptr,"%lld",&((*(Books+n_books)).isbn));
            fscanf(ptr,"%s",(*(Books+n_books)).title);
            fscanf(ptr,"%s",(*(Books+n_books)).author);
            fscanf(ptr,"%d",&((*(Books+n_books)).quantity));
            fscanf(ptr,"%f",&((*(Books+n_books)).price));
            Date date;
            fscanf(ptr,"%d",&date.month);
            fscanf(ptr,"%d",&date.year);
            (*(Books+n_books)).date = date;

            printf("Name:%s\n",Books[n_books].title);
            printf("ID:%lld\n",Books[n_books].isbn);
            printf("price:%f\n",Books[n_books].price);
            printf("date:%d-%d\n",Books[n_books].date.month,Books[n_books].date.year);
            printf("quantity:%d\n",Books[n_books].quantity);
            printf("author:%s\n",Books[n_books].author);

            n_books++;
            Books=realloc(Books,sizeof(Book)*n_books);
        }

        fclose(ptr);
    }
    else {
        printf("Couldnt load books data");
        exit(0);
    }

    free(Books);
    return 0;
}
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • 2
    You might want to investigate other format specifiers. [See here](https://en.cppreference.com/w/c/io/fscanf). %99[^,] might be interesting. – Avi Berger Dec 19 '22 at 19:30
  • 2
    [Why is “while( !feof(file) )” always wrong?](https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong) is worth a read. – Retired Ninja Dec 19 '22 at 19:39
  • Here's an answer of mine that illustrates a generalized dynamic array implementation: [Creation of Dynamic Array of Strings in C](https://stackoverflow.com/a/73737192/5382650) for a similar book implementation. – Craig Estey Dec 19 '22 at 19:39
  • @AviBerger i tried it before and it doesn't work – abdelrahman amr Dec 19 '22 at 20:03
  • Just because you have one problem does not mean you do not have more than one problem. I just tried it on a trimmed down and adjusted version of your code. [It does work for your spaces issue.](https://godbolt.org/z/9vnhjs99Y). It doesn't address anything else. @RetiredNinja mentioned another of your code's issues. A third vital issue is that you have to properly move past the delimiting commas in your input stream. – Avi Berger Dec 19 '22 at 20:38
  • 3
    Not the problem, but: I'd find this code much easier to read and work with if it used normal array notation `fscanf(ptr, "%d", &Books[n_books].quantity);` instead of `fscanf(ptr, "%d", &((*(Books+n_books)).quantity));`. – Steve Summit Dec 19 '22 at 22:31

1 Answers1

1

Your program has the following issues:

  1. The %s conversion format specifier will only read a single word.
  2. You are using while(!feof(ptr)), which is generally wrong (and also wrong in this case).
  3. You are not skipping the , in the input stream.
  4. By using %f, you are telling fscanf to store a float into int price.
  5. The line Books=realloc(Books,sizeof(Book)*n_books); will only allocate enough memory for what you are currently using, but you need to allocate enough for what you are currently using AND for the next book.

Here is a solution which fixes these issues:

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

typedef struct Date
{
    int year;
    int month;

} Date;

typedef struct Book
{
    long long int isbn;
    char title[100];
    char author[100];
    int quantity;
    double price;
    Date date;

} Book;

int main( void )
{
    Book *Books;
    int n_books = 0;

    FILE *fp = fopen( "Books.txt", "r" );
    if ( fp == NULL )
    {
        perror( "fopen" );
        exit( EXIT_FAILURE );
    }

    Books = malloc( sizeof(Book) );
    if ( Books == NULL )
    {
        fprintf( stderr, "Memory allocation error!\n" );
        exit( EXIT_FAILURE );
    }

    while (
        fscanf(
            fp,
            "%lld,%99[^,],%99[^,],%d,%lf,%d-%d",
            &Books[n_books].isbn,
            Books[n_books].title,
            Books[n_books].author,
            &Books[n_books].quantity,
            &Books[n_books].price,
            &Books[n_books].date.month,
            &Books[n_books].date.year
        )
        == 7
    )
    {
        printf( "Name: %s\n", Books[n_books].title );
        printf( "ID: %lld\n", Books[n_books].isbn );
        printf( "price: %lf\n", Books[n_books].price );
        printf( "date: %d-%d\n", Books[n_books].date.month, Books[n_books].date.year);
        printf( "quantity: %d\n", Books[n_books].quantity);
        printf( "author: %s\n", Books[n_books].author);

        n_books++;
        Books = realloc( Books, sizeof(Book) * (n_books+1) );
        if ( Books == NULL )
        {
            fprintf( stderr, "Memory allocation error!\n" );
            exit( EXIT_FAILURE );
        }
    }

    free(Books);

    fclose( fp );

    return 0;
}

For the input specified in the question, this program has the following output:

Name: An Introduction to Organic Chemistry
ID: 9780136019701
price: 3.540000
date: 12-2008
quantity: 10
author: Timberlake Karen
Name: Mathematics for Social Scientists
ID: 9781506304212
price: 4.730000
date: 12-2015
quantity: 7
author: Kropko Jonathan

However, it is also worth noting that calling realloc once per loop iteration is rather inefficient, as realloc may have to copy the entire content of the old memory buffer to the new memory location every time. But this will likely only become a problem if you have hundreds or thousands of books. See this answer of mine to another question for a more efficient algorithm for using realloc.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39