我读过帖子,展示如何使用fseek和ftell来确定文件的大小。
FILE *fp;
long file_size;
char *buffer;
fp = fopen("foo.bin", "r");
if (NULL == fp) {
/* Handle Error */
}
if (fseek(fp, 0 , SEEK_END) != 0) {
/* Handle Error */
}
file_size = ftell(fp);
buffer = (char*)malloc(file_size);
if (NULL == buffer){
/* handle error */
}
我正准备使用这种技术但后来遇到了描述潜在漏洞的link。
该链接建议使用fstat。有人可以对此发表评论吗?
答案 0 :(得分:14)
该链接是CERT的许多无意义的C编码建议之一。他们的理由是基于C标准允许实现的自由,但是POSIX 不允许这样做,因此在你fstat
作为替代的所有情况下都无关紧要。
POSIX要求:
"b"
的{{1}}修饰符无效,即文本和二进制模式的行为相同。这意味着他们对在文本文件上调用UB的担忧是无稽之谈。
文件具有通过写操作和截断操作设置的字节分辨率大小。这意味着他们对文件末尾的空字节随机数的关注是无稽之谈。
可悲的是,随着他们发布的所有废话,很难知道哪些CERT出版物需要认真对待。这很遗憾,因为很多都很严重。
答案 1 :(得分:6)
如果你的目标是找到文件的大小,肯定你应该使用fstat()
或其朋友。这是一种更直接,更具表现力的方法 - 你实际上要求系统告诉你文件的统计数据,而不是更环绕的fseek / ftell方法。
奖励提示:如果您只想知道文件是否可用,请使用access()
而不是打开文件,甚至是对其进行统计。这是一个更简单的操作,许多程序员都不知道。
答案 2 :(得分:4)
不使用fstat
的原因是fstat
是POSIX,但fopen
,ftell
和fseek
是其中的一部分C标准。
可能有一个系统实现了C标准而不是POSIX。在这样的系统上fstat
根本不起作用。
答案 3 :(得分:3)
我倾向于同意他们的基本结论,即您通常不应该直接在代码的主流中使用fseek
/ ftell
代码 - 但是您可能也不应该使用fstat
。如果您想要文件的大小,那么您的大部分代码都应使用明确的直接名称,例如filesize
。
现在, 可能更好地使用fstat
(如果可用)和(例如)FindFirstFile
在Windows上实现(最明显的{{1}平台通常不可用。)
故事的另一方面是fstat
关于二进制文件的许多(大多数?)限制实际上源于CP / M,它没有明确地将文件的大小存储在任何地方。文本文件的结尾由control-Z发出信号。但是,对于二进制文件,您真正知道的是用于存储文件的扇区。在最后一个扇区中,您有一些经常(但不总是)零填充的未使用数据。不幸的是,可能存在重要的零和/或非零的非零值。
如果整个C标准是在批准之前编写的(例如,如果它是在1988年开始并在1989年完成),他们可能完全忽略了CP / M.然而,无论好坏,他们开始在1982年左右的时候开始研究C标准,当时CP / M的使用范围仍然广泛,不容忽视。当CP / M消失时,许多决定已经做出,我怀疑是否有人想重新审视它们。
然而,对于今天的大多数人来说,没有任何意义 - 如果没有大量的工作,大多数代码都不会移植到CP / M;这是要处理的相对次要问题之一。使代码和数据的内存只有48K(左右)的现代程序运行是一个很多更严重的问题(对于大容量存储而言,最大的兆字节左右是另一个严重的问题)。
CERT确实有一个好处:你可能应该不(通常做)查找文件大小,分配那么多空间,然后假设文件内容适合那里。尽管fseek / ftell会使用现代系统为您提供正确的大小,但是当您实际读取数据时,这些数据可能会过时,因此无论如何您都可能超出缓冲区。
答案 4 :(得分:2)
将文件位置指示器设置为文件结束,与
fseek(file, 0, SEEK_END)
一样,具有二进制流的未定义行为(因为 可能的尾随空字符)或任何流 状态相关的编码,不能确定地在初始结束 转变状态。
通过以下方式计算文件大小,一个法律信函的人可能认为可以避免这个UB:
fseek(file, -1, SEEK_END);
size = ftell(file) + 1;
但C标准也说过:
二进制流不需要有意义地支持fseek调用 SEEK_END的价值。
因此,关于fseek / SEEK_END,我们无法解决这个问题。不过,我更喜欢fseek / ftell而不是特定于操作系统的API调用。