使用不带预定义缓冲区的fgets()

时间:2018-06-27 09:44:00

标签: c buffer stdin fgets

我需要再问一个有关从标准输入中阅读的问题。 我正在从标准输入中读取大量的行,但是绝对不知道这是每行的大小。因此,我不想只为一行包含三个char的文件而不是一个使用每行50 Mio的文件的缓冲区提供类似50Mio的缓冲区。 因此,目前我有以下代码:

int cur_max = 2047;
char *str = malloc(sizeof(char) * cur_max);
int length = 0;

while(fgets(str, sizeof(str), stdin) != NULL) {
    //do something with str
    //for example printing
    printf("%s",str);
}

free(str);

因此,我在每行中使用fgets,并且每行的初始大小为2047个字符。 我的计划是在一行达到极限时增加缓冲区(str)的大小。所以我的想法是用长度计算大小,如果当前长度大于cur_max,那么我会将cur_max加倍。 这个想法来自这里Read line from file without knowing the line length 我目前不确定如何使用fgets进行此操作,因为我认为fgets不会按字符进行此char操作,因此我不知道何时增加大小。

2 个答案:

答案 0 :(得分:2)

代码错误

sizeof(str)是指针的大小,例如2、4或8个字节。将fgets()指向的内存大小传递到str@Andrew Henle @Steve Summit

char *str = malloc(sizeof(char) * cur_max);
...
// while(fgets(str, sizeof(str), stdin) != NULL
while(fgets(str, cur_max, stdin) != NULL

环境限制

文本文件fgets()并不是读取超长行的便携式解决方案。

  

实现应支持文本文件,该文件的行至少包含254个字符,包括终止换行符。宏BUFSIZ的值至少应为256 C11§7.21.29

因此,一旦行长超过BUFSIZ - 2,C标准库函数是否可以处理文本文件的代码就由自己决定。

所以要么以二进制形式读取数据,要么使用其他确保所需功能的库,要么依赖希望。

注意:BUFSIZ中定义的<stdio.h>

答案 1 :(得分:1)

POSIX.1 getline()man 3 getline)在几乎所有操作系统的C库中都可用(我所知道的唯一例外是Windows)。读取任意长度的线的循环是

char    *line_ptr = NULL;
size_t   line_max = 0;
ssize_t  line_len;

while (1) {

    line_len = getline(&line_ptr, &line_max, stdin);
    if (line_len == -1)
        break;

    /* You now have 'line_len' chars at 'line_ptr',
       but it may contain embedded nul chars ('\0').
       Also, line_ptr[line_len] == '\0'.
    */
}

/* Discard dynamically allocated buffer; allow reuse later. */
free(line_ptr);
line_ptr = NULL;
line_max = 0;

还有一个相关函数getdelim(),它带有一个额外的参数(在流之前指定),用作记录结束标记。从Unixy / POSIXy环境中读取文件名时特别有用。标准输入,因为您可以使用nul本身('\0')作为分隔符(例如,参见find -print0xargs -0),从而可以正确处理所有可能的文件名。

如果您使用Windows,或者您的文本文件具有不同的换行约定(不仅是'\n',还包括'\n''\r'"\r\n"或{ {1}}),您可以使用我的另一个答案中的getline_universal()函数实现。它与标准"\n\r"getline()的不同之处在于,换行符不包含在它返回的行中。它也留在流中,并被对fgets() next 调用所消耗/忽略。如果您使用getline_universal()来读取文件或流中的每一行,它将按预期工作。