我正在尝试创建一个函数,以使用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函数是通过使用文件名作为第一个命令行参数来启动的。
答案 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
字节)
只要您动态分配原始缓冲区(使用512
,malloc
或calloc
),就可以使用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:上面的代码未经测试,可能包含错误]