-1

i want to read these data and put them to a array of struct but it does not work

#include<stdio.h>
struct book {
    char bookName[50];
    char authorName[50];
    long price;
    int year;
}
main() {
    FILE *data;
    data=fopen("library.txt", "r");
    if (data == NULL) {
        printf("File Could not be opened!\n");
    }
    else {
        struct book myBook;
        struct book books[20];

        int n=0;

        while (!feof(data))
        {
            fscanf(data,"%s***%s***%d***%d\n",&myBook.bookName,&myBook.authorName,&myBook.price,&myBook.year);
            books[n]=myBook;
            n++;
        }

        int i;
        for(i=0;i<n;i++){
            printf("%s - %s - %d - %d \n",books[i].bookName,books[i].authorName,books[i].price,books[i].year);
        }
    }
}

and output is

C - b - 0 - 232159429
programing***Fatemeh - b - 0 - 232159429
Kazemi***15000***1391 - b - 0 - 232159429
C - b - 0 - 232159429
programs***Ali - b - 0 - 232159429
Ahmadpour***20000***1392 - b - 0 - 232159429
Programing***Mona - b - 0 - 232159429
Ghassemi***25000***1389 - b - 0 - 232159429
C - b - 0 - 232159429
programing - b - 0 - 232159429
(advanced)***Sara - b - 0 - 232159429
Hamidi***40000***1385 - b - 0 - 232159429

but my real data is

C programing***Fatemeh Kazemi***15000***1391
Cprograms***Ali Ahmadpour***20000***1392
Programing***Mona Ghassemi***25000***1389
C programing (advanced)***Sara Hamidi***40000***1385

what should i do? it looks fscanf only works with spaces but i need to use *** to seperate my data

ali
  • 11
  • 1

2 Answers2

0

Don't use fscanf(). The error checking is too difficult or incomplete.

while (!feof(data)) does not insure the the following read will be successful.

// while (!feof(data)){  
//  fscanf(data,"%s***%s***%d***%d\n",....

Use fgets() to read a line of file input and then parse.

size_t n = 0;
// Work with an ample sized buffer
char buf[sizeof (struct book) * 2];
while (n < 20 && fgets(buf, sizeof buf, data)) {

Now parse the data looking for "***"

  char *sep[3];
  sep[0] = strstr(buf, "***");
  if (sep[0] == NULL) break;
  sep[1] = strstr(sep[0] + 3, "***");
  if (sep[1] == NULL) break;
  sep[2] = strstr(sep[1] + 3, "***");
  if (sep[2] == NULL) break;

Let us pre-fill myBook with zeros - useful for debugging and insure null characters for the strings.

  struct book myBook;

Insure separators are not too far apart

  if (sep[0] - buf >= sizeof myBook.bookName) break;
  memcpy(myBook.bookName, buf, sep[0] - buf);    //
  if (sep[1] - sep[0] - 3 >= sizeof myBook.authorName) break;   
  memcpy(myBook.authorName, sep[0] + 3, sep[1] - sep[0] - 3);    

Use atol() or ...

  myBook.price = atol(sep[1] + 3);

... strtol(), or sscanf() for more error checking. Maybe check for valid year values?

  char *endptr;
  errno = 0;
  myBook.year = strtol(sep[2] + 3, &endptr, 10);
  if (sep[2] + 3 == endptr) break;
  if (errno) break;
  if (myBook.year < year_MIN || myBook.year > year_MAX) break;

Ok, code made it

   books[n] = myBook;
   n++;      
 }

but i need to use *** to separate my data

If "*" and "**" never occur, code could use the following which has less error checking. Always check the return values of input functions.

while (n < 20 && fgets(buf, sizeof buf, data)) {
  if (sscanf(buf, "%49[^*]***%49[^*]***%ld***%d",
      myBook.bookName, myBook.authorName, &myBook.price, &myBook.year) != 4) {
    break;  // badly formatted data
  }
  books[n] = myBook;
  n++;      
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

This program reads your data in the current format and produces the output as '-' delimited entries (as in the printf statement you provided):

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

#define MAXBUF 500

int parse_string(char* s,char* a[]);

struct book {
    char bookName[50];
    char authorName[50];
    long price;
    int year;
};

int main() {

    FILE *data;
    char buf[MAXBUF];
    char *sp, *words[MAXBUF];
    int nw;

    data=fopen("library.txt", "r");
    if (data == NULL) {
        printf("File Could not be opened!\n");
    }
    else {
        struct book myBook;
        struct book books[20];

        int n=0;

        // Read each line into buf and parse it into words, 
        // assign words into structure fields   
        while((sp = fgets(buf, MAXBUF, data))!= NULL){
            nw=parse_string(buf, words);

            // If number of words different than number of
            // fields in the structure exit with an error
            if (nw!=4){
                printf("Error - number of parsed words does not match number of fields in the structure!\n");
                exit(1);
            }

            // Assign values, convert strings to long and int
            strncpy(myBook.bookName, words[0], strlen(words[0])+1);
            strncpy(myBook.authorName, words[1], strlen(words[1])+1);

            myBook.price = atol(words[2]);
            myBook.year = atoi(words[3]);

            // Add to book array
            books[n] = myBook;

            n++;
        }

       // Print 
       int i;
       for(i=0;i<n;i++){
           printf("%s - %s - %ld - %d \n",books[i].bookName,books[i].authorName,books[i].price,books[i].year);
       }
    }
}


/* Function to parse a line of input into an aray of words */
/* s - pointer to the char array (line) to be parsed
 * a - array of pointers to parsed elements 
 * returns number of elements in a*/
int parse_string(char* s, char* a[])
{
    int nw,j;
    a[0] = strtok(s,"*\t\n\r\v\f");
    nw = 1;
    while((a[nw]= strtok(NULL,"*\t\n\r\v\f"))!=NULL)
        nw++;
    return nw;
}

Like suggested in the comments, this program uses fgets to read the file line at a time. It then uses a parsing function to parse each line into words based on given delimiters. The parsing function is small and uses strtok and hardcoded delimiter set. Based on your input, it does not use the space as a delimiter and instead uses the *. It allows for other delimiters like reasonably new lines and maybe not as reasonably tabs.

Next step is assignment of each word to its field in the book structure. This is preceded by a check whether the number of words return from the parsing function (nw) is equal to to the current number of fields). Finally, if it is - it assigns the members with any necessary conversions to myBook and adds this entry to book array.

Some improvements that can be left out as an exercise would be enabling command line argument with input file name rather than hardcoding it. Writing the output to a file would also be good in which case output file would be the second command line argument. Finally, as mentioned in the comments you could and should provide some input size checking against the sizes of the arrays you are using.

atru
  • 4,699
  • 2
  • 18
  • 19
  • A short coming to `strtok(s,"*\t\n\r\v\f");` is that a single `'*'` also acts as a field delimiter. – chux - Reinstate Monica Dec 29 '17 at 21:12
  • @chux true, but I assumed the OP wanted plain information in the output and that the `*` would not be the part of that information. It looks like the OP is using `*` instead of a space to allow for space-separated names in his fields which makes sense. – atru Dec 29 '17 at 21:19
  • @ali glad to help, this is the way I would normally do it for myself. If you have any questions about the details of this code, feel free to ask, I'll add them to the answer. Study the pieces in detail, they will be useful in the future. – atru Dec 29 '17 at 21:23
  • 1
    @ali can the book name really contain a `*` in it? if that's the case then this will need some changes. – atru Dec 29 '17 at 21:27
  • there is no * in the default data of the question , and nothing is mentioned – ali Dec 29 '17 at 21:32
  • i have a question . what does the s parameter do in the parse_string function – ali Dec 29 '17 at 21:36
  • ok, I got confused when re-reading the comments under your question. To me it seems you answered to @chux that it is possible - if it is not, consider deleting that comment and answering with no or even add it to your question. – atru Dec 29 '17 at 21:36
  • s is the line from file you are parsing, it is a pointer to a string/char array. The particular way it is parsed is a common usage of strtok. I can explain but you can also look it up easily and find some explanation to your liking. – atru Dec 29 '17 at 21:38