obstack,get和getline

问题描述:

我想从标准输入中得到一行。据我所知,我们不应该使用获取在获取手册页中说的:obstack,get和getline

不要使用gets()。因为不知道 是什么数据,所以事先知道有多少个字符会被读取(),而 因为gets()会继续存储超过 缓冲区末尾的字符,所以使用它是非常危险的。它已经被用于破解计算机安全的 。使用fgets()代替。

它表明我们可以使用fgets()来代替。 fgets()的问题在于我们并不知道用户输入的大小,并且fgets()正如人们所说的从流中只读取一个小于字节的字节:

fgets()read in最多只有一个小于流 中的大小字符,并将它们存储到s指向的缓冲区中。阅读在EOF或换行符后停止 。如果读取换行符,则将其存储到 缓冲区中。终止空字节('\ 0')存储在缓冲区中最后一个 字符之后。

还有其使用POSIX函数getline(它使用realloc的更新缓冲器大小,所以我们可以读取与来自输入流的任意长度的任意的字符串作为人所述的另一种方法):

或者,在调用getline()之前,* lineptr可以包含一个 指向大小为n字节的malloc(3)分配的缓冲区的指针。如果 缓冲区不足以容纳该行,getline()会使用realloc(3)调整其大小 ,并根据需要更新* lineptr和* n。

最后是其使用obstack作为libc的手动另一种方法表示:

除了的释放的顺序这一个约束,obstacks是 完全一般:一个obstack可以包含任意数量的对象 任何大小。它们用宏来实现,因此只要对象通常很小,分配通常会非常快。和每个对象的唯一 空间开销是开始在合适的边界每个 对象所需的填充...

所以我们可以使用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; 
} 

所以我的问题是: 是可以安全使用obstack这样吗? 它是否像使用POSIX getline? 我在这里错过了什么吗?有什么缺点? 为什么我不应该使用它? 在此先感谢。

+2

如果您有权访问POSIX'getline'函数,请使用它。否则,自己实现它并不难,在循环中读取字符(就像你一样),然后根据需要重新分配内存。不需要使用像'obstack'这样的非标准功能。 –

+1

“*阅读循环中的字符(就像你做的那样)*” -​​ 或者恕我直言,更好的办法是用'fgets()'读取块,如果最后读取的字符不是换行符,则增加缓冲区大小。 –

+0

@Someprogrammerdude,我知道我可以使用POSIX getline,也知道如何实现getline。我只是想知道使用obstack有什么缺点,除非是非标准的,我认为这不是因为它在gnu libc中(至少我不在乎,因为我使用的是Ubuntu和Fedora)。 –

fgets没有缺陷超过gets。它只是迫使你确认必须知道缓冲区的大小。而是需要你以某种方式神奇地知道长度的输入(可能是恶意的)用户将要输入到你的程序中。这就是为什么从C编程语言中删除gets的原因。现在是非标准,而fgets标准和便携式。

至于知道这条线的长度,POSIX说一个实用程序必须准备处理适合尺寸为LINE_MAX的缓冲区的行。因此你可以这样做:

char line[LINE_MAX]; 
while (fgets(line, LINE_MAX, fp) != NULL) 

和任何生成问题的文件都不是标准的文本文件。在实践中,如果你不盲目地认为缓冲区中的最后一个字符总是'\n'(它不是),那么一切都会好起来的。


getline是一个POSIX标准函数。 obstack是一个不可移植的GNU libc扩展。 getline是为了有效读取文件中的行而构建的,obstack不是,它被构建为通用。使用obstack时,该字符串在内存/最终位置中的连接不正确,直到您致电obstack_finish

如果在POSIX上使用getline,则在需要最大限度移植的程序中使用fgets;对于建立在fgets上的非POSIX平台寻找getline的仿真。

为什么我不应该使用它?

那么,如果你关心可移植性,你不应该使用getline()。如果您仅专门针对POSIX系统,则您需要应该使用getline()

至于obstacks,它们特定于GNU C库,它可能已经是避免它们的一个强有力的理由(它进一步限制了可移植性)。另外,它们并不意味着用于此目的。

如果您的目标是便携,请使用fgets()。这不是太复杂,编写一个基于fgets()类似getline()功能 - 这里有一个例子:

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

,如果发现这其中除去换行,并返回一个新分配的缓冲区(其中主叫方必须free()) 。适应您的需求。这可以通过增加缓冲区大小来改善只有当缓冲区被完全填满时,只需多一点代码...

+1

恕我直言,最好的建议是在存在'getline'函数时使用股票(可能会优化),并在没有时返回到自定义实现。一个简单的编译时定义足以选择使用什么实现,可移植性,同时使用大部分平台支持。 –

+0

getline的好处在于它允许重用已经分配的缓冲区,以便读入* one *缓冲区,始终将其增长到最长的行长度,然后'strdup'字符串** iff **需要。 –

+0

@AnttiHaapala它取决于你的用例,但你当然也可以自己实现这种行为。 –