我想从stdin获得一条线。据我所知,我们永远不应该像获取的手册中所说的那样使用获取:
永远不要使用gets()。因为不知道就不可能说出来 数据预先获取多少个字符()将读取,和 因为gets()将继续存储超过结束的字符 缓冲,使用起来非常危险。它已经习惯了 打破计算机安全。改为使用fgets()。
它表明我们可以使用fgets()代替。 fgets()的问题在于我们事先并不知道用户输入的大小,而fgets()从流中读取的字符串只有一个小于字节的字节,正如man所说:
fgets()从流中读取最多一个小于大小的字符 并将它们存储到s指向的缓冲区中。阅读停止 在EOF或换行符之后。如果读取换行符,则将其存储到 缓冲区。终止空字节(' \ 0')存储在最后一个字节之后 缓冲区中的字符。
还有另一种方法是使用POSIX getline(),它使用realloc来更新缓冲区大小,这样我们可以从输入流中读取任意长度的字符串,如man所说:
或者,在调用getline()之前,* lineptr可以包含一个 指向malloc(3)分配缓冲区* n字节大小的指针。如果 缓冲区不足以容纳线,getline()调整它的大小 使用realloc(3),根据需要更新* lineptr和* n。
最后还有另一种使用obstack的方法,因为libc手册说:
除了释放顺序的这一约束之外,还有障碍物 完全一般:一个障碍物可以包含任意数量的物体 任何尺寸。它们是用宏实现的,所以分配是 通常非常快,只要对象通常很小。而且 每个对象只有空间开销是启动每个对象所需的填充 对象在合适的边界上......
因此我们可以对任何大小的任何对象使用obstack,分配非常快,只需要很小的空间开销,这不是什么大问题。我编写这段代码来读取输入字符串而不知道它的长度。
#include <stdio.h>
#include <stdlib.h>
#include <obstack.h>
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
int main(){
unsigned char c;
struct obstack * mystack;
mystack = (struct obstack *) malloc(sizeof(struct obstack));
obstack_init(mystack);
c = fgetc(stdin);
while(c!='\r' && c!='\n'){
obstack_1grow(mystack,c);
c = fgetc(stdin);
}
printf("the size of the stack is: %d\n",obstack_object_size(mystack));
printf("the input is: %s\n",(char *)obstack_finish(mystack));
return 0;
}
所以我的问题是: 使用像这样的障碍物是否安全? 是否喜欢使用POSIX getline? 我在这里错过了什么吗?有什么缺点吗? 为什么我不应该使用它? 提前谢谢。
答案 0 :(得分:2)
fgets
对gets
的没有缺点。它只是强迫你确认你必须知道缓冲区的大小。 gets
反而要求您以某种方式神奇地知道输入(可能是恶意的)用户将输入您的程序的长度。这就是为什么{C {1}}已从C编程语言中删除的原因。它现在是非标准,而gets
标准且可移植。
至于预先知道行的长度,POSIX说必须准备一个实用程序来处理适合LINE_MAX
大小的缓冲区的行。因此你可以这样做:
fgets
任何产生问题的文件都不是标准文本文件。在实践中,如果你不盲目地假设缓冲区中的最后一个字符总是char line[LINE_MAX];
while (fgets(line, LINE_MAX, fp) != NULL)
(它不是),那么一切都会很好。
'\n'
是POSIX标准函数。 getline
是GNU libc扩展,不可移植。 obstack
是为了有效地从文件中读取行而构建的,getline
不是,它被构建为泛型。对于obstack
,字符串在内存中/最终位置不正确连续,直到您调用obstack
。
如果在POSIX上使用obstack_finish
,请在需要最大程度移植的程序中使用getline
;为fgets
上构建的非POSIX平台寻找getline
仿真。
答案 1 :(得分:2)
为什么我不应该使用它?
好吧,如果你关心可移植性,你就不应该使用getline()
。如果您专门定位POSIX系统,应使用getline()
。
对于 obstacks ,它们特定于GNU C库,这可能已经是避免它们的强有力理由(它进一步限制了可移植性)。而且,它们并不意味着用于此目的。
如果您的目标是可移植性,请使用fgets()
。根据{{1}}编写类似于getline()
的函数并不太复杂 - 这是一个例子:
fgets()
如果找到换行符,则删除换行符并返回一个新分配的缓冲区(调用者必须#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CHUNKSIZE 1024
char *readline(FILE *f)
{
size_t bufsize = CHUNKSIZE;
char *buf = malloc(bufsize);
if (!buf) return 0;
char *pos = buf;
size_t len = 0;
while (fgets(pos, CHUNKSIZE, f))
{
char *nl = strchr(pos, '\n');
if (nl)
{
// newline found, replace with string terminator
*nl = '\0';
char *tmp = realloc(buf, len + strlen(pos) + 1);
if (tmp) return tmp;
return buf;
}
// no newline, increase buffer size
len += strlen(pos);
char *tmp = realloc(buf, len + CHUNKSIZE);
if (!tmp)
{
free(buf);
return 0;
}
buf = tmp;
pos = buf + len;
}
// handle case when input ends without a newline
char *tmp = realloc(buf, len + 1);
if (tmp) return tmp;
return buf;
}
int main(void)
{
char *input = readline(stdin);
if (!input)
{
fputs("Error reading input!\n", stderr);
return 1;
}
puts(input);
free(input);
return 0;
}
)。适应您的需求。当缓冲区完全填满时,可以通过增加缓冲区大小 来改善它,只需要更多的代码......