声明
void get_line (char* filename)
后,你不能使用在
get_line
函数之外读取和存储的行,因为你没有返回指向行的指针,也没有传递任何指针的地址,这些指针可以用来在调用函数中进行任何分配和读取。
对于任何将未知数量的字符读入单个缓冲区的函数,一个很好的模型(显示返回类型和有用参数)总是 POSIX 的
getline
。你可以使用
fgetc
或
fgets
和一个固定的缓冲区来实现自己的函数。效率上,使用
fgets
仅在最小化所需的
realloc
调用次数方面具有优势。(两个函数将共享相同的低级输入缓冲区大小,例如请参见 gcc 源码中的
IO_BUFSIZ
常量--如果我记得正确的话,它在最近更名后现在是
LIO_BUFSIZE
,但基本上归结为 Linux 上的一个
8192
字节 IO 缓冲区和 Windows 上的
512
字节)
只要您使用
malloc
、
calloc
或
realloc
动态分配原始缓冲区,就可以使用
fgets
连续读取固定缓冲区中的内容,并将读取的字符添加到您分配的行中,检查最后一个字符是否为
'\n'
或
EOF
以确定何时完成。每次迭代都用
fgets
读取固定大小的字符缓冲区,并在进行时使用
realloc
重新分配您的行,将新字符附加到末尾。
在重新分配时,始终使用临时指针进行realloc
。这样,如果内存不足,realloc
返回NULL
(或由于任何其他原因而失败),您不会使用NULL
覆盖当前分配块的指针,从而创建内存泄漏。
一种灵活的实现方式是将固定缓冲区作为VLA进行大小调整,使用定义的SZINIT
(如果用户传递0
)或用户提供的大小来分配line
的初始存储空间(作为指向指针的指针),然后根据需要重新分配,成功时返回读取的字符数,失败时返回-1
(与POSIX getline
相同):
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,
maxc = *n ? *n : szinit,
eol = 0,
nc = 0;
char buf[maxc];
clearerr (fp);
while (fgets (buf, maxc, fp)) {
nc = strlen (buf);
if (idx && *buf == '\n')
break;
if (nc && (buf[nc - 1] == '\n')) {
buf[--nc] = 0;
eol = 1;
}
void *tmp = realloc (*line, idx + nc + 1);
if (!tmp)
return idx ? (ssize_t)idx : -1;
*line = tmp;
memcpy (*line + idx, buf, nc + 1);
idx += nc;
if (eol)
break;
}
return (feof (fp) && !nc) || ferror (fp) ? -1 : (ssize_t)idx;
}
(
注意: 由于
nc
已经保存了
buf
中当前字符的数量,因此可以使用
memcpy
将
buf
的内容附加到
*line
中,而无需再次扫描终止的
nul-character) 请仔细查看并告诉我是否有进一步的问题。
基本上,您可以将其用作 POSIX
getline
的替代品(虽然它不会像 POSIX
getline
一样高效,但也不算太差)。
string line = Console.ReadLine()
就能实现? - undefined