-2

I'm making a program called "Book manager", all in need now is to create functions to save and load Books(structs) from file. I'm wondering how to read them. Here's my struct definition:

typedef
struct book {
    char * title;
    char * authorName;
    char * authorSurname;
    int releaseYear;
    char * genre;
    int flags[10];
} Book;

I've made a function that allow me to read one book from file, but it's awful and i dont get it how to read a set of Books. I want to read it to my array.

 Book allBooks[LIBRARY_SIZE]; 

Sample file that i want to read from looks:

Christine,Stephen,King,1980,Horror
Harry Potter,J.K,Rowling,2000,Fantasy

Any ideas?

Edit: This is what i tried to do to read simple Book(at the beginning profesor said it would be enough) Dont commit suicide while reading this.

void loadBook(Book * book, FILE * plik){
    char jakasTablica[50];
    char jakasTablica2[50];
    char jakasTablica3[50];
    char jakasTablica4[50];
    int i = 0;
    char znak;
    fscanf(plik,"%c",&znak);
    while(znak != ','){
        jakasTablica[i] = znak;
        i += 1;
        fscanf(plik,"%c",&znak);
    }
    jakasTablica[i] = '\0';

    strcpy(book->title, jakasTablica);

    i = 0;
    fscanf(plik,"%c",&znak);
    while(znak != ','){
        jakasTablica2[i] = znak;
        i += 1;
        fscanf(plik,"%c",&znak);
    }
    jakasTablica2[i] = '\0';

    strcpy(book->authorName, jakasTablica2);

    i = 0;
    fscanf(plik,"%c",&znak);
    while(znak != ','){
        jakasTablica3[i] = znak;
        i += 1;
        fscanf(plik,"%c",&znak);
    }
    jakasTablica3[i] = '\0';

    strcpy(book->authorSurname, jakasTablica3);

    i = 0;
    fscanf(plik,"%c",&znak);
    while(znak != ','){
        jakasTablica4[i] = znak;
        i += 1;
        fscanf(plik,"%c",&znak);
    }
    jakasTablica4[i] = '\0';

    strcpy(book->genre, jakasTablica4);

    i = 0;
    int zmienna = 0;
    fscanf(plik, "%d", &zmienna);
    book->releaseYear = zmienna;
}
Seki
  • 11,135
  • 7
  • 46
  • 70
Karol.T
  • 65
  • 1
  • 9
  • Why don't you show what you have try? – Stargateur Jan 20 '17 at 19:44
  • Please post your code that you use to read a single book from the file. – Emmanuel Mathi-Amorim Jan 20 '17 at 19:47
  • Have you worked out an algorithm yet? If so, explain it and tell us where you ran into trouble implementing it. If not, show us what you've got so far and explain where you're stuck. – David Schwartz Jan 20 '17 at 19:47
  • I've edited and showed working code i used to read simple book. – Karol.T Jan 20 '17 at 19:51
  • 2
    How do you handle book titles with commas in them ("The Lion, the Witch and the Wardrobe", for example)? Do you want the variable-length fields shown, or do you want fixed-size structures? If you want variable-length fields, you'll probably read a line of data and then parse that. Using standard CSV technology, you'd have `"The Lion, the Witch, and the Wardrobe",C.S.,Lewis,1950,Fantasy` with double quotes around the field containing commas. CSV parsing (and writing) code would handle that automatically. – Jonathan Leffler Jan 20 '17 at 21:15

5 Answers5

3

To answer how to read each comma separated line you'd do the following:

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

int main (void)
{
    FILE *fp;
    char a[30], b[30], c[30], d[30], e[30];
    char line[100];

    fp=fopen("./data.txt", "r");
    fgets( line, sizeof(line), fp );
    sscanf( line, "%29[^,],%29[^,],%29[^,],%29[^,],%29[^,]", a,b,c,d,e );

    printf( "%s %s %s %s %s\n", a,b,c,d,e);
    fclose( fp );
    return( 0 );
}

This will read only one line, but you can put it into a loop and process that data one line at a time as needed. This may not be the fastest way to do it, but it is pretty simple as long as you know the format for each line.

What about book titles with ,'s in them? Maybe you'd be better off using a CSV parsing library. If you use a library like that one you'd have to put "'s around any field that contains ,'s before writing it out to the text file so the CSV parser can handle it.


You can use binary file I/O using fread()/fwrite() structs directly. You will have to use a fixed size structure. Below is a simple example. It doesn't do all the needed error checking but it should be enough to get you started.

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

typedef struct
{
    char title[60];
    char author[20];
    int year_published;
    int pages;
}Book;



int main (void)
{
    FILE *fp;
    Book b1,b2;
    Book book;

    strcpy( b1.title, "Title Book 1" );
    strcpy( b1.author, "Book 1 Author" );
    b1.year_published = 1991;
    b1.pages = 123;

    strcpy( b2.title, "Title Book 2" );
    strcpy( b2.author, "Book 2 Author" );
    b2.year_published = 1992;
    b2.pages = 456;


    fp=fopen("./books.dat", "w+");
    if( fp )
    {
        fwrite( &b1, sizeof( b1 ), 1, fp );
        fwrite( &b2, sizeof( b2 ), 1, fp );
        fclose( fp );
        printf( "Wrote file\n" );
        printf( "Now reopen the file and read the structs\n" );

        fp = fopen( "./books.dat", "r" );
        if( fp )
        {
            fread( &book, sizeof(book), 1, fp );
            printf("%s %s %d %d\n", book.title, book.author, book.year_published, book.pages);

            fread( &book, sizeof(book), 1, fp );
            printf("%s %s %d %d\n", book.title, book.author, book.year_published, book.pages);

            fclose( fp );
        }
        else
        {
            printf(" Failed to open created data file.\n");
        }
    }
    else
    {
        printf( "Failed to open file\n" );
    }

    return 0;
}
Chimera
  • 5,884
  • 7
  • 49
  • 81
  • Yeah seems like a great solution, the only think is that i wish it to write it as a standard .txt so i could modify my 'book's database' even when i don't run program. – Karol.T Jan 20 '17 at 20:59
  • @Karol.T I've added a section on how to read you comma separated lines. – Chimera Jan 20 '17 at 21:11
  • @DavidBowling Modified based on your comment. Thank you. – Chimera Jan 20 '17 at 22:18
2

OP wants to read a line of comma separated text into a custom struct.

Strongly recommend to separate out the reading from the parsing.

  char buffer[LINE_SIZE];
  if (fgets(buffer, sizeof buffer, plik) == NULL) return ...;

Then parse the line and return a value indicating 1:success, 0:fail or end-of-file:EOF

// void loadBook(Book * book, FILE * plik){
int loadBook(Book * book, FILE * plik) {

Now parse buffer[]. There are many approaches with various pros and cons. The following makes heavy use of "%n" to save the offset of the scan up to that point.

#define LINE_SIZE 512
// scan until encountering a comma or linefeed
#define FS "%*[^,\n]%n"
#define FI "%d %n"

int loadBook(Book * book, FILE * plik) {
  memset(book, 0, sizeof *book);  // zero fill
  char buffer[LINE_SIZE];
  if (fgets(buffer, sizeof buffer, plik) == NULL) {
    return EOF;
  }

  int n1, n2, n3, n4, n5, i;
  n5 = 0;
  sscanf(buffer, FS "," FS "," FS "," FI "," FS, 
      &n1, &n2, &n3, &i, &n4, &n5);

  // If scan was incomplete or did not finish on a \n, return 0
  if (n5 == 0 || buffer[n5] != '\n') return 0;

  // Let us use a check for a sane year as a data qualifier test
  // https://wiganlanebooks.co.uk/blog/interesting/
  //    10-of-the-oldest-known-surviving-books-in-the-world/
  if (i < -600 || i > 2999) return 0;

  buffer[n1] = 0; book->title = strdup(buffer);
  buffer[n2] = 0; book->authorName = strdup(buffer + n1 + 1);
  buffer[n3] = 0; book->authorSurname = strdup(buffer + n2 + 1);
  book->releaseYear = i;
  buffer[n5] = 0; book->genre = strdup(buffer + n4 + 1);
  return 1;
}

strdup() is a common, yet non-standard function that allocates memory and duplicates a string. Sample code Be sure to free allocated space when done with book.

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Interesting approach. So, if the input line is larger than the buffer, or if the data for the `year_published` field is out of the test range, the function returns a value of `0`. – ad absurdum Jan 20 '17 at 22:21
  • Learned something new about the `%n`. Thank you. – Chimera Jan 20 '17 at 22:26
  • 1
    @DavidBowling Yes, the `"%n"` at the end of the format can be used as a simple test for _complete_ scanning success. But maybe the buffer is too small: [max book name](http://www.news18.com/news/india/worlds-longest-book-title-1022-words-and-no-spaces-276244.html) – chux - Reinstate Monica Jan 20 '17 at 22:27
  • They way you parse the line is superior to my simple approach. – Chimera Jan 20 '17 at 22:39
  • @Chimera Thanks. "There are many approaches". The trick is that data input typically should be qualified as early as possible. OP's simple post does not detail max sizes, allowable name, titles with a ',', valid categories, etc. Depending on those unstated issues would drive what is the _better_ approach and how to maintain it. – chux - Reinstate Monica Jan 20 '17 at 22:46
1

This is how i solved my problem using Chimera's code.

void readBooks(Book * List){
FILE * plik;
plik=fopen("booksList.txt","r");
int iloscKsiazekDoDodania;
char pom;
pom = fgetc(plik);

while(!feof(plik)){
    if(pom == '\n') iloscKsiazekDoDodania += 1;
    pom = fgetc(plik);
}
printf("%d\n", iloscKsiazekDoDodania);
fclose(plik);
plik = fopen("booksList.txt","r");


for(int i = 0; i < iloscKsiazekDoDodania; i++){
    char a[50], b[50], c[50], d[50];
    char line[100];
    int x;
    fgets(line, 99, plik);
    sscanf(line, "%[^,],%[^,],%[^,],%[^,],%d", a,b,c,d,&x);
    addRandom(a,b,c,x,d,List);
}

fclose(plik);
}

and my writing function:

void saveBooks(Book * List){
FILE * plik;
plik=fopen("booksList.txt","w");
for(int i = 0; i < allBooksSize; i++){
    fprintf(plik,"%s,", List[i].title);
    fprintf(plik,"%s,", List[i].authorName);
    fprintf(plik,"%s,", List[i].authorSurname);
    fprintf(plik,"%s,", List[i].genre);
    fprintf(plik,"%d\n", List[i].releaseYear);
}
fclose(plik);
}

Thanks for helping.

Karol.T
  • 65
  • 1
  • 9
0

Change your Book definition into:

typedef
struct book {
    char title[50];
    char authorName[50];
    char authorSurname[50];
    int releaseYear;
    char genre[50];
    int flags[10];
} Book;

and then in loadBook:

... 
int n;
for (n= 0; n < LIBRARY_SIZE; ++n) {
    ...  
    Book[n].title[i] = znak;
    ...

etc.

Good luck.

-1

You can use the following example to read structs from a file in C:

if ( myFile.is_open( ) )
{
    while ( getline( myFile, reading ) )
    {
        stringstream eachline( reading ) ;
        while ( eachline( ) )
        {
            string x = ',';
            getline( eachline, StoreData, x ) ;

            // read if float or read else word from file
            // Increment++ here
        }
    }
}
Grant Miller
  • 27,532
  • 16
  • 147
  • 165
Danny
  • 1
  • 1