0

How can I check if a given FILE* contains a string in C running on Linux (if it matters)?

The string must consist of the whole line it's on. For example, this:

jfjfkjj
string
jfjkfjk

would be true; but this:

jffjknf
fklm...string...lflj
jfjkfnj

wouldn't. I'm essentially looking for an internal alternative to system("grep -x file")

Billy
  • 1,177
  • 2
  • 15
  • 35
  • 6
    Read line by line and compare. – Eugene Sh. Jun 27 '18 at 18:03
  • 1
    You can use functions like `getline` and `strcmp`, etc. – lurker Jun 27 '18 at 18:10
  • @lurker Okay, I'll read their manpages. – Billy Jun 27 '18 at 18:11
  • `while(fgets( ....) != NULL)` and remember to [remove any trailing newline](https://stackoverflow.com/questions/2693776/removing-trailing-newline-character-from-fgets-input/28462221#28462221). – Weather Vane Jun 27 '18 at 18:14
  • @WeatherVane I just now saw your comment, that's indeed the code I came up with just now. I know this is somewhat off-topic, but what's the difference between `while(fgets( ....) != NULL)` and `while(!fgets( ....))`? – Billy Jun 27 '18 at 18:40
  • There is no functional difference, but there are questions which ask if `NULL` is guaranteed to be `0`. Using `NULL` makes the code more readable as it is clear that comparison is for a pointer. Suppose your pointers are 64-bit but `int` is 32-bit. Does `!fgets` create more work at runtime, or will a good compiler do the work for you? – Weather Vane Jun 27 '18 at 18:49
  • system("file fname"); – purec Jun 27 '18 at 19:21
  • @WeatherVane Of course `while(fgets( ....) != NULL)` and `while(!fgets( ....))` are different. Certainly OP meant to compare `while(fgets( ....) != NULL)` and `while(fgets( ....))` - no `!`. In this 2nd code comparison, if `NULL` is 0 or not is immaterial as they both function the same. The _null pointer constant_ `NULL`, if not already a _null pointer_, is converted to one before the compare. – chux - Reinstate Monica Jun 27 '18 at 20:40
  • @chux brain fart. OP's `while(!fgets( ....))` was wrong. – Weather Vane Jun 27 '18 at 20:43
  • @WeatherVane, yeah you're right I meant `while(fgets( ....))`. – Billy Jun 28 '18 at 01:36

1 Answers1

0

This reads a file line by line and checks if the line matches the string supplied in argument 1 (argv[1]) after every read. If so, it sets the bool infile (bools defined in <stdbool.h>) to true.

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

int main(int argc, char **argv[]) {
    char    *filepath = "/path/to/file";
    bool    infile = false;
    char    *line = NULL;
    size_t  len = 0;
    ssize_t read;

    FILE    *fp = fopen(filepath, "r");

    if (!fp) {
        fprintf(stderr, "Failed to open %s\n", filepath);
        return 1;
    }

    while ((read = getline(&line, &len, fp)) != -1) {
        line[strcspn(line, "\n")] = 0;
        if (!strcmp(line, argv[1])) {
            infile = true;
            break;
        }
    }
    fclose(uuidfp);

    if (line)
        free(line);

    return 0;
}
Billy
  • 1,177
  • 2
  • 15
  • 35
  • If you're looking to improve performance, you might want to hash the target line and compare the hash to each line in the file first. You'll still need to do the `strcmp()` in case of hash collisions, but collisions should be rare, unless you expect adversarial input. – EOF Jun 27 '18 at 19:10
  • 1
    @EOF I wouldn't expect that'd be worth the trouble. – Steve Summit Jun 27 '18 at 19:18
  • @SteveSummit I'd expect it depends. An even simpler test would be to use the return value of `getline()`, which must either be `strlen(argv[1])` or `strlen(argv[1])+1` for the strings to possibly be equal. – EOF Jun 27 '18 at 19:25
  • 2
    A reasonable solution, but has no report about whether the string from `argv[1]` was found in the file. And it has a flaw that you failed to check `argc` to ensure there is an `argv[1]` supplied. Moreover, the `main` argument is incorrect; `char **argv[]` should be either `char *argv[]` or `char **argv`. – Weather Vane Jun 27 '18 at 19:36
  • @EOF [2nd idea](https://stackoverflow.com/questions/51068916/determine-if-a-file-contains-a-string-in-c#comment89130023_51069789) is a good one. – chux - Reinstate Monica Jun 27 '18 at 20:46
  • Minor point: `free(NULL)` is OK so `if (line) free(line);` can be `free(line);`. No functional problem leaving it as is either. Close to a style issue. – chux - Reinstate Monica Jun 27 '18 at 20:47
  • Using `line[strcspn(line, "\n")] = 0;` is a nice one-liner to remove the `'\n'`which can spend time checking each element of the string. Yet code does have the line length `read` for an even faster test. – chux - Reinstate Monica Jun 27 '18 at 20:54
  • @EOF I ended up using strcasecmp() in my program, so that wouldn't work. – Billy Jul 28 '18 at 01:08
  • `strcmp` for some reason doesn't work for me. I used: `strstr(line, argv[1]) != NULL` instead. – lepe Jul 08 '19 at 04:29