为什么 fgets 函数被弃用?

10

来自GNU C编程教程

fgets("file get string")函数类似于gets函数。此函数已被弃用——这意味着它已经过时,强烈建议不要使用它——因为它是危险的。它是危险的,因为如果输入数据包含null字符,则无法确定。除非您知道数据不可能包含null,否则请勿使用fgets。不要用它来读取用户编辑的文件,因为如果用户插入了null字符,则应正确处理它或打印清晰的错误消息。尽可能使用getlinegetdelim而不是fgets

我认为fgets函数遇到\0\n会停止;为什么这个手册页面会建议null字节是“危险的”,当fgets应该能够正确处理输入?此外,getlinefgets之间有什么区别,并且在C99或将来的C标准中,fgets函数是否真的被视为弃用


1
fgets()在遇到空字节时不会停止,只有在空间用尽、遇到换行符或者到达EOF时才会停止。 - Jonathan Leffler
2个回答

17
不,fgets在C99或当前标准C11中并没有被弃用。但是那个教程的作者说得对,fgets遇到NUL字符时不会停止读取,并且没有机制报告其读取了这样的字符。

fgets函数从由指针stream指向的流中最多读取n个字符数减1个字符的数据,并将其放入由指针s指向的数组中。在新行字符(保留)或文件结束后,不会再读取其他字符。

(§7.21.7.2)

GNU的getdelimgetline已经被标准化为POSIX 2008,所以如果你的目标平台是POSIX,那么使用它们可能不是一个坏主意。

编辑我曾认为在面对NUL字符时绝对没有安全的方法可以使用fgets,但R..(见评论)指出确实有:

char buf[256];

memset(buf, '\n', sizeof(buf));  // fgets will never write a newline
fgets(buf, sizeof(buf), fp);

现在在buf中查找最后一个非\n字符。尽管如此,我并不推荐这种方法。


2
@VilhelmGray: 是的,而且它不会告诉你它这样做了。无法确定您找到的第一个'\0'是否是由 fgets 添加的。 - Fred Foo
8
空字节不应该出现在文本文件中。fgets() 函数是为处理文本文件设计的:不建议将 fgets() 用于二进制数据文件。 - pmg
3
@VilhelmGray说:stdio 倾向于过分信任用户——它没有预料到文本文件中可能出现的长行、空字符等情况。它来自上世纪70年代,当时防御性编程并不像现在这么重要(没有互联网,也没有脚本小子试图入侵你的系统,也没有新手用户会因为弄坏了文本文件而狂发垃圾信息)。 - Fred Foo
1
@R..:可能吧,但是当I/O不是瓶颈时,我更喜欢我的代码干净利落。 - Fred Foo
4
“// fgets will never write a newline”这句话不太清楚。在许多fgets()调用中,确实会向缓冲区写入'\n'。您的意思是“// fgets永远不会读取换行符后面的内容”吗? - chux - Reinstate Monica
显示剩余10条评论

8
这只是GNU的宣传。在任何官方意义上,fgets都没有被弃用。然而,gets是危险且已被弃用。

5
“gets”实际上被从2011年的ISO C标准中删除了。(它在C99中没有被正式弃用或过时。) - Keith Thompson
3
ISO/IEC 9899:1999 TC3的第7.26.9节规定:“gets函数已过时且不建议使用”。因此,我相信在C99中正式废弃了gets函数。 - CB Bailey
那种语言是在 TC 中添加的,还是在 C99 的原始出版物中就有了? - R.. GitHub STOP HELPING ICE
2
@R..:这是由TC3添加的。 - Keith Thompson
TC3于2007年发布(根据其封面为2007年11月15日)。因此,gets()在1999年并未被弃用,但在C99的更新版本中于2007年被弃用,比C11发布约4年。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接