将fgets与realloc()一起使用

将fgets与realloc()一起使用,c,malloc,realloc,C,Malloc,Realloc,我试图创建一个函数,使用fgets()从文本文件中读取一行,并使用malloc()将其存储在动态分配的字符*中,但我不确定如何使用realloc()因为我不知道这一行文字的长度,也不想猜测这一行可能达到的最大大小的神奇数字 #include "stdio.h" #include "stdlib.h" #define INIT_SIZE 50 void get_line (char* filename) char* text; FILE* file = fopen(filena

我试图创建一个函数,使用
fgets()
从文本文件中读取一行,并使用
malloc()
将其存储在动态分配的字符*中,但我不确定如何使用
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]);
}
我计划用这行文字做其他事情,但为了保持简单,我刚刚打印了它,然后释放了内存


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

一个可能的解决方案是使用两个缓冲区:一个临时缓冲区,在调用
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;
    }
}
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;
}
[Discalimer:上面的代码未经测试,可能包含bug]

该函数正是您需要的

像这样使用它:

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;
    }
}
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;
}

如果你真的想自己实现这个函数,你可以这样写:

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;
    }
}
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;
}
#包括
#包括
char*get_line()
{
INTC;
/*缓冲区的当前大小是多少*/
尺寸=5;
/*缓冲区填满了多少*/
大小\u t读取\u大小=0;
/*第一次分配,其结果应进行测试*/
char*line=malloc(大小);
如果(!行)
{
佩罗尔(“马洛克”);
回流线;
}
第[0]行=“\0”;
c=fgetc(标准偏差);
而(c!=EOF&&c!='\n')
{            
行[读取大小]=c;
++读取大小;
如果(读取大小==大小)
{
尺寸+=5;
char*test=realloc(行、大小);
如果(!测试)
{
perror(“realloc”);
回流线;
}
直线=测试;
}
c=fgetc(标准偏差);
}
行[读取大小]='\0';
回流线;
}

声明为
无效获取行(char*filename)
,您永远不能使用在
get\u line
函数之外读取和存储的行,因为您不会返回指向行的指针,也不会传递任何指针的地址,而这可能会使任何分配和读取在调用函数中变为可见

POSIX
getline
始终是任何函数将未知数量的字符读入单个缓冲区的好模型(显示返回类型和有用参数)。您可以使用
fgets
fgetc
和固定缓冲区来实现自己的功能。效率有利于使用
fgets
,只要它能最大限度地减少所需的
realloc
调用次数。(这两个函数将共享相同的低级输入缓冲区大小,例如,请参见gcc source
IO_BUFSIZ
constant——如果我回忆起来,在最近的名称更改之后,它现在是
LIO_BUFSIZE
,但基本上可以归结为Linux上的
8192
字节IO缓冲区和windows上的
512
字节)

只要动态分配原始缓冲区(使用
malloc
calloc
realloc
),您可以使用固定缓冲区连续读取,方法是使用
fgets
将读取到固定缓冲区的字符添加到分配行,并检查最后一个字符是
'\n'
还是
EOF
,以确定何时完成。只需在每次迭代时使用
fgets
realloc
读取一个固定缓冲区大小的字符,并在末尾添加新字符

重新分配时,始终使用临时指针
realloc
。这样,如果内存不足并且
realloc
返回
NULL
(或由于任何其他原因失败),则不会用
NULL
覆盖指向当前分配块的指针,从而造成内存泄漏

一种灵活的实现,它使用定义的缓冲区大小的
SZINIT
(如果用户通过
0
)或用户提供的大小将固定缓冲区大小调整为VLA,以分配
行的初始存储(作为指向字符的指针通过),然后根据需要重新分配,成功时返回读取的字符数或失败时返回读取的字符数(与POSIX
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
中当前的字符数,
memcpy
可用于将
buf
的内容附加到
*行
,而无需再次扫描终止的nul字符)请仔细查看,如果有进一步的问题,请告诉我


基本上,您可以使用它作为POSIX
getline
的替代品(虽然效率不高,但也不错)

根据代码中的注释,您对初始缓冲区太短的情况感兴趣,对吗?请查看。我猜你基本上就是想要这样。阅读一行无限长的文字有一个缺点,那就是允许极端代理压倒系统资源——不要成为黑客的目标。有一个合理的上限是防御性的编码。你不喜欢一种只允许你做
string line=Console.ReadLine()
的语言吗?请注意
getline
是一个只支持POSIX的函数,它不可移植。@Someprogrammerdude的“POSIX”和“not portable”在术语上有些矛盾。只有当“不可移植”实际上意味着“几乎在任何地方都可用,但在Windows上不可用”时,这才是正确的从不
realloc
指针本身,而是使用临时指针,例如
void*tmp=realloc(行、大小)
@DavidC.Rankin我同意,但请注意,如果
realloc
失败,则调用
exit()
是的,这是处理发生故障(或