while (1) {
len = getline(&line_buf, &line_max, stdin);
if (len < 0)
break;
// line_buf has len characters of data in it, and line_buf[len] == '\0'.
// If the input contained embedded '\0' bytes in it, then strlen(line_buf) < len.
// Normally, strlen(line_buf) == len.
}
free(line_buf);
line_buf = NULL;
line_max = 0;
if (!feof(stdin) || ferror(stdin)) {
// Not all of input was processed, or there was an error.
} else {
// All input processed without I/O errors.
}
1条答案
按热度按时间hyrbngr71#
相关标准是IEEE Std 1003.1, also called POSIX.1,特别是其系统接口,以及受影响的函数here列表。
我推荐Linux man pages online。不要被它的名字吓住,因为C函数(在第2、3节中描述)有一个部分 Conforming to,它指定了哪些标准对该特性进行标准化。如果是C89、C99等,则它包含在C标准中;如果是POSIX.1,则为POSIX;如果是SuS,则在POSIX之前的单一Unix规范中;如果是4.3BSD,则在旧BSD版本4.3中;如果是SVr 4,则在Unix System V版本4中,依此类推。
Windows实现了自己的C扩展,并避免支持任何POSIX或SuS,但移植了4.3BSD的一些细节。请使用Microsoft文档来查找。
在Linux中,如果在执行
#include
语句之前定义了某些预处理器宏,C库就会公开这些特性。这些在man 7 feature_test_macros中有描述。我通常在POSIX中使用#define _POSIX_C_SOURCE 200809L
,偶尔在GNU C扩展中使用#define _GNU_SOURCE
。getline()
是一个很好的接口,除了习惯于Microsoft/Windows的程序员使用时,它并不“泄漏”,比如在没有Microsoft专用扩展的情况下,无法将宽字符输出到控制台(因为他们显然不想将该实现放在fwide()中)。最常见的使用模式是初始化未分配的缓冲区和合适的行长度变量:
然后,当你读一行时,C库可以自由地重新分配缓冲区,以容纳该行所需的任何大小。例如,你的逐行读文件循环可能如下所示:
注意
free(NULL)
是安全的,它什么也不做。这意味着我们可以在循环后安全地使用free(line_buf); line_buf = NULL; line_max = 0;
--事实上,在任何我们想要的地方!--来丢弃当前的行缓冲区。如果需要,下一个使用相同变量的getline()
或getdelim()
调用将分配一个新的行缓冲区。上面的模式从不会泄漏内存,并且可以正确地检测文件处理过程中的所有错误,从I/O错误到没有足够的RAM可用(或当前进程所允许的),尽管它无法区分它们:它也不会有假错误,除非你在你自己添加的处理代码中跳出循环。
因此,任何声称
getline()
“有漏洞”的说法都是反POSIX、亲微软的宣传。出于某种原因,微软一直拒绝在他们自己的C库中实现这些功能,尽管他们很容易做到。如果你想复制一行的一部分,我推荐使用strdup()或strndup(),它们也是POSIX.1-2008函数。它们返回一个动态分配的字符串副本,后者只复制指定的字符数(如果字符串在此之前没有结束);在所有情况下,如果函数返回一个非NULL指针,则动态分配的字符串以nul '\0'终止,并且在不再需要时,应该像上面的getline()缓冲区一样使用
free()
释放。如果您还必须在Microsoft上运行代码,一个好的选择是在没有提供
getline()
的架构和操作系统上实现您自己的getline()
。(您可以使用Pre-defined Compiler Macros Wiki来查看如何检测在特定架构、操作系统或编译器上编译的代码。)一个
getline()
实现的例子可以写在fgets()
的上面,增长缓冲区并读取更多的内容(追加到现有缓冲区),直到缓冲区以换行符结束。但是,它不能真正处理数据中嵌入的“\0”字节;要做到这一点,并正确实现getdelim()
,您需要使用fgetc()
逐字符读取数据。