在没有预定义缓冲区的情况下使用 fgets()

Using fgets() without predefined buffer

我需要再问一个关于从标准输入读取的问题。 我正在从 stdin 读取一大串行,但绝对不知道每行的大小是多少。所以我不想像 50Mio 这样的缓冲区只用于具有三个字符行的文件,而不是每行使用这些 50 Mio 的文件。 所以目前我有这段代码:

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 不是按字符执行此操作,所以我不知道何时增加大小。

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 ('[=10=]').
       Also, line_ptr[line_len] == '[=10=]'.
    */
}

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

还有一个相关的函数getdelim(),它接受一个额外的参数(在流之前指定),用作记录结束标记。在 Unixy/POSIXy 环境中读取文件名时特别有用。标准输入,因为您可以使用 nul 本身 ('[=14=]') 作为分隔符(参见 find -print0 or xargs -0),允许正确处理所有可能的文件名。

如果您使用 Windows,或者如果您的文本文件具有不同的换行约定(不仅是 '\n',还有 '\n''\r'"\r\n",或 "\n\r"),您可以使用我的另一个答案中的 getline_universal() 函数实现。它与标准 getline()fgets() 的不同之处在于换行符不包含在 returns 行中; nextgetline_universal() 的调用也将其留在流和 consumed/ignored 中。如果您使用 getline_universal() 读取文件或流中的每一行,它将按预期工作。

代码不正确

sizeof(str) 是指针的大小,例如 2、4 或 8 个字节。将 str 指向的内存大小传递给 fgets()@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() 不是阅读超长行的便携解决方案。

An implementation shall support text files with lines containing at least 254 characters, including the terminating new-line character. The value of the macro BUFSIZ shall be at least 256 C11 §7.21.2 9

因此,一旦行长度超过 BUFSIZ - 2,关于 C 标准库函数是否可以处理 文本文件.[=21= 的代码将自行决定]

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

注:BUFSIZ定义在<stdio.h>