将fgets与realloc()一起使用

时间:2018-10-25 08:19:20

标签: c malloc realloc

我正在尝试创建一个函数,以使用fgets()从文本文件中读取一行并将其存储在使用malloc()的动态分配char *中,但是我不确定如何请使用realloc(),因为我不知道这行文本的长度,也不想仅仅猜测一个魔术数字来表示该行可能的最大大小。

#include "stdio.h"
#include "stdlib.h"
#define INIT_SIZE 50

void get_line (char* filename)

    char* text;
    FILE* file = fopen(filename,"r");

    text = malloc(sizeof(char) * INIT_SIZE);

    fgets(text, INIT_SIZE, file);

    //How do I realloc memory here if the text array is full but fgets
    //has not reach an EOF or \n yet.

    printf(The text was %s\n", text);

    free(text);

int main(int argc, char *argv[]) {
    get_line(argv[1]);
}

我打算用一行文本来做其他事情,但是为了保持简洁,我只打印了一下然后释放了内存。

也:main函数是通过使用文件名作为第一个命令行参数来启动的。

3 个答案:

答案 0 :(得分:3)

您正在寻找getline函数。

像这样使用它:

char *line = NULL;
size_t n;
getline(&line, &n, stdin);

如果您真的想自己实现此功能,则可以编写如下内容:

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

char *get_line()
{
    int c;
    /* what is the buffer current size? */
    size_t size = 5;
    /* How much is the buffer filled? */
    size_t read_size = 0;
    /* firs allocation, its result should be tested... */
    char *line = malloc(size);
    if (!line) 
    {
        perror("malloc");
        return line;
    }

    line[0] = '\0';

    c = fgetc(stdin);
    while (c != EOF && c!= '\n')
    {            
        line[read_size] = c;            
        ++read_size;
        if (read_size == size)
        {
            size += 5;
            char *test = realloc(line, size);
            if (!test)
            {
                perror("realloc");
                return line;
            }
            line = test;
        }
        c = fgetc(stdin);
    }
    line[read_size] = '\0';
    return line;
}

答案 1 :(得分:1)

使用void get_line (char* filename)的声明,您将永远无法利用在get_line函数外部读取和存储的行,因为您不会返回指向行的指针并且不会传递 任何指针的地址,该指针可以用来进行任何分配并在调用函数中读回。

对于任何将未知数量的字符读入单个缓冲区的函数而言,一个好的模型(显示返回类型和有用的参数)始终是POSIX getline。您可以使用fgetc中的fgets和固定缓冲区来实现自己的功能。效率仅在尽可能减少所需fgets调用次数的情况下才支持使用realloc。 (这两个函数将共享相同的低级输入缓冲区大小,例如,请参见gcc源IO_BUFSIZ常量-如果我记得最近的名称更改后,它现在是LIO_BUFSIZE,但基本上可以归结为{在Linux上为{1}}字节IO缓冲区,在Windows上为8192字节)

只要您动态分配原始缓冲区(使用512malloccalloc),就可以使用realloc通过固定缓冲区连续读取,并添加字符读入分配的行的固定缓冲区中,并检查最后一个字符是fgets还是'\n',以确定何时完成。只需每次迭代EOF读取一个固定的字符缓冲区,并在每次行进fgets时一行,将新字符追加到末尾即可。

重新分配时,总是 realloc 使用临时指针。这样,如果内存不足,并且realloc返回realloc(或由于其他任何原因而失败),则不会用NULL覆盖当前分配的块的指针来创建一个内存泄漏。

一种灵活的实现,它使用已定义的NULL缓冲区大小(如果用户通过SZINIT)或用户提供的大小为VLA分配固定缓冲区大小,以为VLA分配初始存储0(作为指向char指针的指针传递),然后根据需要重新分配,返回成功读取或失败时line读取的字符数(与POSIX -1相同)像这样完成:

getline

注意:,由于/** fgetline, a getline replacement with fgets, using fixed buffer. * fgetline reads from 'fp' up to including a newline (or EOF) * allocating for 'line' as required, initially allocating 'n' bytes. * on success, the number of characters in 'line' is returned, -1 * otherwise */ ssize_t fgetline (char **line, size_t *n, FILE *fp) { if (!line || !n || !fp) return -1; #ifdef SZINIT size_t szinit = SZINIT > 0 ? SZINIT : 120; #else size_t szinit = 120; #endif size_t idx = 0, /* index for *line */ maxc = *n ? *n : szinit, /* fixed buffer size */ eol = 0, /* end-of-line flag */ nc = 0; /* number of characers read */ char buf[maxc]; /* VLA to use a fixed buffer (or allocate ) */ clearerr (fp); /* prepare fp for reading */ while (fgets (buf, maxc, fp)) { /* continuall read maxc chunks */ nc = strlen (buf); /* number of characters read */ if (idx && *buf == '\n') /* if index & '\n' 1st char */ break; if (nc && (buf[nc - 1] == '\n')) { /* test '\n' in buf */ buf[--nc] = 0; /* trim and set eol flag */ eol = 1; } /* always realloc with a temporary pointer */ void *tmp = realloc (*line, idx + nc + 1); if (!tmp) /* on failure previous data remains in *line */ return idx ? (ssize_t)idx : -1; *line = tmp; /* assign realloced block to *line */ memcpy (*line + idx, buf, nc + 1); /* append buf to line */ idx += nc; /* update index */ if (eol) /* if '\n' (eol flag set) done */ break; } /* if eol alone, or stream error, return -1, else length of buf */ return (feof (fp) && !nc) || ferror (fp) ? -1 : (ssize_t)idx; } 已包含nc中的当前字符数,因此buf可用于附加{{1} }至memcpy,而无需再次扫描终止的 nul-character 。)仔细检查一下,让我知道是否还有其他问题。

本质上,您可以将其用作POSIX buf的替代产品(尽管效率不高-也不算差)

答案 2 :(得分:0)

一种可能的解决方案是使用两个缓冲区:调用fgets时使用的一个临时缓冲区;然后重新分配一个,然后将临时缓冲区附加到其中。

也许是这样的:

char temp[INIT_SIZE];  // Temporary string for fgets call
char *text = NULL;     // The actual and full string
size_t length = 0;     // Current length of the full string, needed for reallocation

while (fgets(temp, sizeof temp, file) != NULL)
{
    // Reallocate
    char *t = realloc(text, length + strlen(temp) + 1);  // +1 for terminator
    if (t == NULL)
    {
        // TODO: Handle error
        break;
    }

    if (text == NULL)
    {
        // First allocation, make sure string is properly terminated for concatenation
        t[0] = '\0';
    }

    text = t;

    // Append the newly read string
    strcat(text, temp);

    // Get current length of the string
    length = strlen(text);

    // If the last character just read is a newline, we have the whole line
    if (length > 0 && text[length - 1] == '\n')
    {
        break;
    }
}

[Discalimer:上面的代码未经测试,可能包含错误]