1

I'm trying to selectively filter a text file by a string which is input to the standard input.

I would like to know why the following code does not work and how to fix it:

void get_filtered_list() {
    FILE *f;
    f = fopen("presentlist.txt", "r"); 
    printf("Enter the city by which you want to select lines:\n");
    char stringToFind[20]; 
    fgets(stringToFind, sizeof(stringToFind), stdin);
    char line[160];
    while (!feof(f)) {
        fgets(line, sizeof(line), f); 
        if (strstr(line, stringToFind) != NULL) {
            printf("%s", line);
        }
    }
    fclose(f);
}

This code above is trying to take a text file, opening that file, then reading the file line by line, and for each line executing the strstr() function with the current line of the file as argument 1 as a string, and the given name of the city as argument 2 as a string.

However what I get as a result is the ENTIRE contents of the file printed (and the last line prints twice, though this is a separate issue and I know the fix to this part).

The C book I'm reading states that the strstr() function is used to find a needle string in a haystack string, so it's the C equivalent of the C++ substr() function.

strstr() takes argument 1 as the haystack and argument 2 as the needle.

I first read in from the standard input into the needle, then line by line I check whether strstr() returns NULL or not (it should return NULL if the needle is not found in the haystack) and if it returns something other than NULL that means it found the substring in the string and it should only print the line THEN.

Instead it prints all of the lines in the file. Why?

If I switch it to f(strstr(line, stringToFind)) instead then it prints absolutely nothing.

Why?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
user1966576
  • 83
  • 1
  • 10
  • I'm not sure why you compare `strstr` with [`substr`](http://en.cppreference.com/w/cpp/string/basic_string/substr). It's more like [`find`](http://en.cppreference.com/w/cpp/string/basic_string/find). – Jongware Oct 26 '15 at 20:21

1 Answers1

6

You do not find the string because you did not strip the trailing '\n' from the string read into stringToFind by fgets. Actually, you will find the string if and only if it is the last word on a line.

You can remove the linefeed with this:

#include <string.h>

stringToFind[strcspn(stringToFind, "\n")] = '\0';

There are other ways to strip the linefeed, but be aware that if the last line of the file does not end with a linefeed, there will not be one in the buffer filled by fgets, therefore you cannot just overwrite the last character of the line. For your problem, it would be a good idea to remove all whitespace characters at the beginning and at the end of stringToFind.

Also check this question: Why is “while ( !feof (file) )” always wrong?

Testing the end of file with while (!feof(f)) will catch the end of file too late: fgets will fail and you do not test its return value, so the last line of the file will appear to be handled twice. The correct way to write this loop is this:

while (fgets(line, sizeof(line), f)) {
    if (strstr(line, stringToFind) != NULL) {
        printf("%s", line);
    }
}

Not also that lines longer than 159 characters will be split by fgets and will cause incorrect output if they contain the searched string, especially if the string itself is split.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Yes I am finding that if I enter the last word it works. But why?? and how do I remove the newline? do I just subtract -1 from the fgets length? – user1966576 Oct 26 '15 at 20:14
  • @user1966576 `strtok(line, "\n")` should suffice. Note that this doesn't work if line has only the `'\n'` character. – Petross404 Nov 09 '21 at 19:28
  • @Petross404: `strtok()` is not recommended to strip the trailing newline precisely because it would fail on empty lines. – chqrlie Nov 10 '21 at 19:09