在标准C中,fseek
/ftell
是唯一的选择。任何其他操作都至少部分依赖于程序运行所在的特定环境。不幸的是,正如你提供的文章所述,这种方式也存在问题。
我猜想你可以一直读取文件直到EOF,并沿途进行跟踪 - 例如使用fread()
。
将文件位置指示器设置为文件结尾,例如fseek(file,0,SEEK_END),对于二进制流(由于可能存在尾随空字符)或具有状态相关编码的任何流而言都具有未定义行为,这些流不保证以初始换档状态结束。
和二进制流不必支持whence值为SEEK_END的fseek调用。
- Alexey FrunzeSEEK_END
的fseek
是未定义行为,并调用不在ISO C和程序中的函数也是未定义行为。解决这个问题以及大多数日常问题需要从自己的眼中移除ISO C的限制。 - Kazfseek(file, -1, SEEK_END)
)- 根据标准,这种行为似乎是可以的。 - Agnius Vasiliauskasfseek(stream, 0, SEEK_END)
是未定义行为,引用了一个脱离上下文的脚注。fseek(stream, 0, SEEK_END)
变成了针对宽向流的未定义行为。而对于字节流则没有 §7.19.2/5 这样的规定。SEEK_END
可能无法被“有意义地”支持的原因。然而,没有真实世界的实现会如此糟糕;此外,POSIX禁止这样做。 - R.. GitHub STOP HELPING ICEfread
读取吗? - R. Martinho Fernandes使用fstat - 需要文件描述符 - 可以从FILE*
的fileno中获取 - 因此,大小与其他细节都在你的掌握之中。
即。
fstat(fileno(filePointer), &buf);
其中filePointer
是FILE *
而buf
则是
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
fstat
的功能是可用的。 - Ed HealFILE*
写入过内容的文件上使用 fstat()
,由于尚未写入缓存数据,它可能会返回错误的文件大小。请注意避免此类情况。 - David Givenstat
在那里不会报告“错误的大小”——它实际上是文件的大小(因为未缓冲的更改尚未被写入)。 - sehe有时候无法避免编写特定于平台的代码,尤其是当您必须处理与平台相关的内容时。文件大小取决于文件系统,所以通常我会使用本地文件系统API来获取该信息而不是使用fseek / ftell操作。为了不使应用程序逻辑混杂具体平台细节并且使代码易于移植,我会创建自己的通用包装器。
这篇文章存在一些逻辑问题。
它(正确地)指出了某些 C 函数的使用行为不符合 ISO C 的定义。但是,为了避免这种未定义的行为,文章提出了一个解决方案:用特定于平台的函数替换该使用方式。然而,根据 ISO C 的规定,使用特定于平台的函数也是未定义的。因此,这个建议并没有解决未定义行为的问题。
我手头的 1999 年标准中的引用证实了所谓的行为确实是未定义的:
二进制流不需要有意义地支持 SEEK_END 值的 fseek 调用。[ISO 9899:1999 7.19.9.2 第 3 段]
但是未定义的行为并不意味着“不好的行为”;它只是 ISO C 标准没有定义的行为。并非所有未定义的行为都相同。
一些未定义的行为是语言中可以提供有意义扩展的领域。平台通过定义行为来填补这个空白。
提供一个可工作的fseek
,可以从SEEK_END
进行搜索,这是一种替代未定义行为的扩展示例。可以确认给定平台是否支持从SEEK_END
进行fseek
,如果有,则可以使用它。
提供一个单独的函数,如lseek
,也是替代未定义行为的扩展(调用不在ISO C中且未在C程序中定义的函数的未定义行为)。如果可用,则可以使用它。
请注意,那些具有类似POSIX lseek
的功能的平台也很可能具有从SEEK_END
工作的ISO C fseek
。还要注意,在无法从二进制文件上的fseek
进行SEEK_END
搜索的平台上,可能的原因是这是不可能做到的(无法提供API来执行此操作,这就是为什么C库函数fseek
无法支持它的原因)。
因此,如果fseek
在给定平台上提供了所需的行为,则程序无需进行任何更改;将其更改为使用该平台的特殊功能是一种浪费。另一方面,如果fseek
未提供行为,则很可能什么也没有提供。
请注意,即使包括程序中不存在的非标头文件也是未定义的行为。(通过省略行为的定义。)例如,如果以下内容出现在C程序中:
#include <unistd.h>
行为在此之后没有定义。[请参见下面的参考文献。] 预处理指令 #include
的行为是被定义的。但这会产生两种可能性:要么头文件 <unistd.h>
不存在,需要进行诊断;要么该头文件存在,但其内容并不为所知(就 ISO C 而言,库中没有记录过这样的头文件)。在这种情况下,包含指令会引入一段未知的代码块,并将其合并到转译单元中。无法定义未知代码块的行为。
#include <platform-specific-header.h>
是语言中一个逃生舱口,用于在特定平台上执行任何操作。
点形式:
<unistd.h>
的行为。一个未定义的 ISO C 程序可以是一个良定义的 POSIX C 程序。#include <pascal.h>
的未定义行为如何引入Pascal关键字进行链接。] http://groups.google.com/group/comp.lang.c/msg/e2762cfa9888d5c6?dmode=source
fseek
/ftell
(如果你使用POSIX,实际上是fseeko
/ftello
,这样你就可以处理大文件)是确定文件大小的首选方式。基于stat
的替代方法将无法确定某些非常规文件的大小(如块设备(磁盘分区等)),而这些文件确实具有明确定义的大小。 - R.. GitHub STOP HELPING ICEFILE* fp = fopen("teste.txt", "a"); size_t sz = ftell(fp);
- Tiago Vieira