Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Book mistake? (Head first C)

Tags:

c

fgets

In the book Head First C, fist edition, there is this progam:

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

char tracks[][80] = {
    "I left my hearth in Harvard Med School",
    "Newark, NewarK - a wonderful town",
    "Dancing with a  Dork",
    "From here to maternity",
    "The girl from Iwo Jima",
};

void find_track(char search_for[]) {
    int i;

    for(i = 0; i < 5; i++) {
        if (strstr(tracks[i], search_for)) {
            printf("Track %i: '%s'\n", i, tracks[i]);
        }
    }
}

int main() {
    char search_for[80];
    printf("Search for: ");
    fgets(search_for, 80, stdin);
    find_track(search_for);
    return 0;
}

However, when compiled and tested, you don't get the expected results. After breaking my head for a bit, I decided to look for the documentation for fgets, I discovered that the function reads up to an including a newline character, which is why no matter what I search for, I never get the expected result. However, in the book they say the program works when tested. Is this a book error, or am I missing something?

PS. the error can be easily fixed using scanf, which becomes obvious once you know why the program doesn't work as expected.

PS2. I remember C++ has some syntax to ignore the newline character. Does C have something similar?

like image 961
Buzu Avatar asked Mar 12 '23 21:03

Buzu


2 Answers

I also encountered the error, but the newest version has corrected the error. In fact, the function 'char *fgets(char *s, int size, FILE *stream) works like what you said. It only read up to size-1. If it encounters a null character('\0'), it only add ('\0') to the buffer. So when we used the fuction find_track(), we can't get anything because of search_for has contained ('\0'). There are some solutions to handle it.

  • scanf("%79s", search_for). Attention, don't only use %s because you wil encounter the same situation like fgets().
  • scanf("%79[^\n]", search_for), it has some difference with the former one. The advantage that you can avoid the blank problem.

PS: The first one, you can add a new fuction to avoid the blank problem. Like this:

void remove_tail_newline(search_for)
{ size_t len = strlen(str);
   if(len > 0 &&str[len-1] =='\n') str[len -1] ='\0';
}
like image 160
Ghoster Avatar answered Mar 24 '23 19:03

Ghoster


You're right; this is indeed an error in the book.

I recommend against using scanf for user input. It is hard to use correctly, error detection is non-trivial (if it is possible at all), and error recovery is impossible.

All user input should be done by reading a line first, then analyzing it afterwards (e.g. using strtol or sscanf). Reading a line can be done with fgets, but unfortunately there's no syntax or feature for ignoring the \n.

What you can do instead is:

if (fgets(line, sizeof line, stdin)) {
    line[strcspn(line, "\n")] = '\0';  // remove trailing '\n', if any
    do_stuff_with(line);
}
like image 23
melpomene Avatar answered Mar 24 '23 20:03

melpomene