Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dynamic buffer size for reading input

I am trying to create a program that will read line by line from stdin, search that line for the start and end of a given word and output all the matching words. Here is the code:

int main()
{
    char buffer[100];
    char **words = NULL;
    int word_count = 0;

    while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
        int length = strlen(buffer);
        if (buffer[length - 1] == '\n') {
            word_count = count_words(buffer, FIRSTCHAR);
            if (word_count > 0) {
                words = get_words(buffer, FIRSTCHAR, LASTCHAR);
                for (int i = 0; i < word_count; ++i) {
                    printf("%s\n", words[i]);
                    free(words[i]);
                }
                free(words);
            }
        }
    }
    return 0;
}

I got the basic functionality working, but I am relying on fgets() with a fixed buffer size. What I would like is to dynamically allocate a memory buffer with a size based on the length of each line.

I can only see one way of going about solving it, which is to iterate over input with fgetc and increment a counter until end of line and use that counter in place of sizeof(buffer), but I don't know how I would get fgetc to read the correct relevant line.

Is there any smart way of solving this?

like image 534
Dennis Fagerblad Avatar asked Oct 25 '25 09:10

Dennis Fagerblad


1 Answers

but I am relying on fgets() with a fixed buffer size. What I would like is to dynamically allocate a memory buffer with a size based on the length of each line

I did wrote a version of fgets for another SO answer that reads the whole line and returns a malloc allocated pointer with the contents of the whole line. This is the code:

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

char *fgets_long(FILE *fp)
{
    size_t size = 0, currlen = 0;
    char line[1024];
    char *ret = NULL, *tmp;

    while(fgets(line, sizeof line, fp))
    {
        int wholeline = 0;
        size_t len = strlen(line);

        if(line[len - 1] == '\n')
        {
            line[len-- - 1] = 0;
            wholeline = 1;
        }

        if(currlen + len >= size)
        {
            // we need more space in the buffer
            size += (sizeof line) - (size ? 1 : 0);
            tmp = realloc(ret, size);
            if(tmp == NULL)
                break; // return all we've got so far
            ret = tmp;
        }

        memcpy(ret + currlen, line, len + 1);
        currlen += len;

        if(wholeline)
            break;
    }

    if(ret)
    {
        tmp = realloc(ret, currlen + 1);
        if(tmp)
            ret = tmp;
    }

    return ret;
}

The trick is to check if the newline was read. If it was read, then you can return the buffer, otherwise it reallocates the buffer with sizeof line more bytes and appends it to the buffer. You could use this function if you like.

An alternative would be if you are using a POSIX system and/or are compiling with GNU GCC, then you can use getline as well.

void foo(FILE *fp)
{
    char *line = NULL;
    size_t len = 0;

    if(getline(&line, &len, fp) < 0)
    {
        free(line); // man page says even on failure you should free
        fprintf(stderr, "could not read whole line\n");
        return;
    }

    printf("The whole line is: '%s'\n", line);

    free(line);

    return;
}
like image 184
Pablo Avatar answered Oct 26 '25 23:10

Pablo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!