Link to home
Start Free TrialLog in
Avatar of 3nigmatic
3nigmatic

asked on

getline function reading from a text file

Hi folks,

I`m doing a project with a few friends where we create a virtual pub quiz using C.

I have a few files which I read into the console, most of which I am fine with.  The file I am having trouble with is the actual question file.

This file contains 20 questions, each question is followed by 4 answers [ example below ]:

Where were the 1970 football World Cup finals held ?
Mexico,Spain,Brazil,England

What I am trying to do is obviously read this file into an array, then display each question and the answers on a seperate line [ as it is in the text file ] rather than all on one line like it currently is.

The code I am working with is below:


void readquiz() {
      
      int i;
      
      FILE *quiz_ptr;
      
      quiz_ptr = fopen("quiz.txt", "r");
      
      if(quiz_ptr != NULL) {
            
            i=0;
            
            while (fscanf(quiz_ptr, "%s", var[i]) != EOF) {
                  printf("%s ", var[i]);
                  i++;
            }
            
            printf("\n\n");
            fclose(quiz_ptr);
      }
}


Currently the data is printed as this :

Where were the 1970 football World Cup finals held ? Mexico,Spain,Brazil,England


I want it to look like this :

Where were the 1970 football World Cup finals held ?
Mexico,Spain,Brazil,England

Problem is I dont know how getline works and cant find much info on it, can anyone help ?

Thanks.
Avatar of Kent Olsen
Kent Olsen
Flag of United States of America image

Hi 3nigmatic,

How is the data file structured?  Are the questions and answers on 1 line or several?

My guess is that everything is on one line.  In that case, you'll have to split the line yourself.  Not too tough, though.


char *Question;
char *Answers;

          while (fscanf(quiz_ptr, "%s", var[i]) != EOF) {
               Question = var[i];
               Answer = strchr (Question, '?');
               if (Answer) {
                 ++Answer;
                 *Answer = 0;
               }
               printf("%s\n%s", Question, Answer);
               i++;



That will do for demonstration purposes, though I suggest that you rethink your array so that you can put the questions and answers in different locations.


Good Luck!
Kent
Avatar of 3nigmatic
3nigmatic

ASKER

Quiestions file data structure is like this:

Where were the 1970 football World Cup finals held ?
Mexico,Spain,Brazil,England
In books by JRR Tolkien, what kind of creatures are Bilbo and Frodo Baggins ?
Hobbits,Orcs,Elves,Humans
Which Italian city is home of the car manufacturer Fiat ?
Turin,Rome,Pisa,Ravena

For 20 questions plus four answers for each question.

The reason I want to use an array is because I will eventually randomise the answer output positions so that people can not just memorise the locations of the correct answer for each question.
ASKER CERTIFIED SOLUTION
Avatar of brettmjohnson
brettmjohnson
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi 3nigmatic,

You can get past the single line effect with just a small change to the printf() statement.

          while (fscanf(quiz_ptr, "%s", var[i]) != EOF) {
               printf("%s\n", var[i]);    // Notice that \n has been added to the format.
               i++;
          }

That will put the question and answer on separate lines.

My suggestion is that you build two arrays.  

char Questions[MAX_QUESTIONS][MAX_LENGTH];
char Answers[MAX_QUESTIONS][MAXLENGTH];

Then in the while loop, read the question then the list of answers.  Answers[0] will apply to Question[0], Answer[6] will apply to Question[6], etc.  Just pick any random number and you have both the question and answer list for that number.


Kent
brettmjohnson,

Thanks for your suggestion I`m looking into how it works etc now.


kdo,

Adding \n to my printf statement does produce a new line, but every new word is on a new line.  This is why I am looking to use getline, or as suggested by brettmjohnson fgets [ which I have also been reading up on ].
With a little modification I went with brettmjohnson's code:

void readquiz() {

     FILE *quiz_ptr;
     char *answs[4];
     char *sep = ",\r\n";
     char line[4096];
       int i;
       int tok;
       int count = 1;

     quiz_ptr = fopen("quiz.txt", "r");
     
     if(quiz_ptr != NULL) {
          while (fgets(line, sizeof(line), quiz_ptr)) {     /* read the next question */
                    printf("%d) ", count);
               fputs(line, stdout);     /* display question */

               if (fgets(line, sizeof(line), quiz_ptr)) {     /* read the answers */
                   /* parse the answers into an array */
                   for(i = 0, tok = strtok(line, sep); i < 4 && tok; i++, tok = strtok(NULL, sep))
                       answs[i] = tok;
                   /* display the answers */
                   printf("\ta) %s\n\tb) %s\n\tc) %s\n\td) %s\n\n", answs[0], answs[1], answs[2], answs[3]);
                           count ++;
               }
          }
              fclose(quiz_ptr);
     }
}

I am a little stuck as to what the variables tok & sep do though, can anyone explain ?

Thanks.
> I am a little stuck as to what the variables tok & sep do though, can anyone explain ?

strtok() tokenizes (breaks up) the string (line) based upon the separator characters (delimiters) in sep.  tok is a pointer to the returned string fragment strtok found.  BTW, strtok() tokenizes in place, so it overwrites the found delimiter (',' or newline) in line with a NUL byte, then returns a pointer to the beginning of that substring, so to will always be a pointer to somewhere in the line buffer (except when  its NULL).  

One caution: strtok() is not thread-safe - it maintains internal state (of its current position in the string) in static local storage (hence passing NULL on subsequent calls).  If you need thread-safety, consider strtok_r() or strsep() instead.

Not also that the code is not robust (for the sake of simplicity and demonstration).   It does not tolerate malformed answers - missing or less than 4 choices.

What is the variable 'count' for?  I appears to be the number of questions+1, but is otherwise unused.
Thanks Brett for the explination, still a little confused but I will work it out ... always good fun.

Err, as for the var count, as you stated, it is basicaly there just to number the questions and thats it.

Why do you ask, is there something wrong with the way I have done it ?
I only asked about 'count' because it is apparently used outside the chunk of code you posted, but as as count of the number of questions ,it is off by one.  To correct the issue, initialize count to 0, not 1.

If I was to set the var count = 0; though, it would show that I had just 19 questions when in fact there are 20.

Or am I missing your point here  ?
You aren't really missing the point, you are just wrong.  
Walk through the code with test files that contain 0, 1, 2,  and 3 questions.