对于C中的文件指针,如何测量当前位置到文件末尾的距离?

Aud*_*cot 1 c position file eof file-pointer

对于给定的文件指针(FILE *),是否可以快速确定从当前位置到文件末尾的距离。

计算实际距离所需的时间不应取决于距离。

比如减法fpos_t,但是fpos_t不是整数,不能进行数值运算。有什么替代方法吗?

Adr*_*ica 6

当您第一次打开文件时,您可以使用fseek()转到文件末尾(但请参阅下面的注释),然后使用ftell()获取位置并将其保存(作为文件的大小)。然后调用rewind()返回到开始。

然后,ftell()可以从您保存的“大小”中减去任何后续调用的返回值,以获得从当前位置到文件末尾的偏移量(距离):

// Given a FILE* fp that's just been opened:
fseek(fp, 0, SEEK_END);
long int endpos = ftell(fp);
rewind(fp); // Or you can use fseek(fp, 0, SEEK_SET);
//...
// Later in your code:
long int dtoend = endpos - ftell(pf);
Run Code Online (Sandbox Code Playgroud)

但请注意,不需要实现SEEK_END:来自fseek上面链接的 cplusplus.com 页面:

  • 允许库实现无意义地支持 SEEK_END(因此,使用它的代码没有真正的标准可移植性)。

只是为了澄清(遵循一些评论):上面的代码要求endpos在文件的打开/读取操作期间保存该值。一个可以寻求到年底,然后恢复到避免这种当前位置在任何时候,但是这将是效率很低的。例如,可以编写一个函数来随时获取到终点的距离:

long int dtoend(FILE *fp)
{
    long int curpos = ftell(fp); // Get current position
    fseek(fp, 0, SEEK_END);      // Go to the file's end
    long endpos = ftell(fp);     // Gets the file's size
    fseek(fp, curpos, SEEK_SET); // Restore previous pos
    return endpos - curpos;      // Return the distance!
}
Run Code Online (Sandbox Code Playgroud)


用于大(> 2GB)文件的注意事项:上述代码使用标准fseekftell函数,这些标准和函数使用long int类型(通常为 32 位宽)作为文件位置;要对较大的文件使用类似的代码,有许多(尽管是特定于平台的)替代方案......

在 Windows 平台上,使用MSVC(或兼容的)编译器,有_ftelli64_fseeki64函数,它们的使用方式几乎与“标准”对应物相同;例如,通过对上述代码进行以下更改:

//...
    int64_t curpos = _ftelli64(fp); // Get current position
    _fseeki64(fp, 0LL, SEEK_END);   // Go to the file's end
    //... and similar changes elsewhere
Run Code Online (Sandbox Code Playgroud)

在Linux系统中,64位调用被实现为ftellofseeko,如果你确保#define _FILE_OFFSET_BITS 64

其他平台/编译器可能会实现上述任一(或两者),或者具有其他一些非常相似的 64 位替换。


注2 -错误处理:由于在评论中指出,调用fseekSEEK_END作为原点参数可以根据不同情节数失败; 例如,如果文件指针是stdin,如果它指的是管道流,或者(在某些系统上)如果文件以文本模式打开。为了处理这种情况,我们应该真正检查fseek调用的返回值,如果调用失败,返回值将是非零的。因此,这是一个 64 位版本的dtoend函数,其中实现了此类错误处理(注意除了MSVCor之外的编译器GNU,您需要为bigseekbigtell函数添加相关的定义宏):

#include <stdio.h>
#include <stdint.h>

#if defined (_MSC_VER) // MSVC/Windows...
    #define bigtell _ftelli64
    #define bigseek _fseeki64
#elif defined (__GNUC__) // GNU/Linux...
    #define _FILE_OFFSET_BITS 64
    #define bigtell ftello
    #define bigseek fseeko
//
// Feel free to add other compiler/platform implementations
//

#else // Unknown platform/compiler - likely to cause warnings!
    #define bigtell ftell
    #define bigseek fseek 
#endif

int64_t dtoend(FILE* fp)
{
    int64_t curpos = bigtell(fp);                   // Saves the file's current position
    if (bigseek(fp, 0LL, SEEK_END) != 0) return -1; // -1 can ONLY be an error condition
    int64_t endpos = bigtell(fp);                   // Retrieve file size (end position)
    bigseek(fp, curpos, SEEK_SET);                  // Restore previously saved position
    return endpos - curpos;                         // Subtract to get distance from end
}
Run Code Online (Sandbox Code Playgroud)

来自上面链接的同一个cplusplus.com页面:

对于以文本模式打开的流,偏移量应为零或先前调用 ftell 返回的值,并且来源必须为 SEEK_SET。如果使用这些参数的其他值调用该函数,则支持取决于特定的系统和库实现(不可移植)。

  • 您还可以解释一些*文件*没有大小/结尾,例如管道、/dev/null 等。 (2认同)
  • 大概到 2050 年,“int64_t”对于文件大小来说应该足够了。 (2认同)