如何:C 中的跨操作系统大文件 IO?

Posted

技术标签:

【中文标题】如何:C 中的跨操作系统大文件 IO?【英文标题】:HOWTO: Cross-Operating-System Large File IO in C? 【发布时间】:2014-04-23 23:28:26 【问题描述】:

简而言之: C 中的跨操作系统、大文件支持非常可怕。

目标:我正在尝试使用“一种方式”(最有可能基于宏)来允许 32 位和 64 位支持大文件。理想情况下,使用 typedef、#ifdef、#(n)defined 等,宏包装器可以允许以 #include 库或一组已定义宏的形式支持基本的大文件。

研究: POSIX 的文件操作在 32 位和 64 位 IO 的 BSD/Mac/Linux 上表现出色,文件大于典型的 2^31 大小,但即使在 Windows 上使用 clang 或 mingw由于 M$ 对 POSIX 的愚蠢实现,我无法利用这些调用(如果这就是我们想要调用的......)。我倾向于在 Windows 上使用 CreateFile()、ReadFile()、WriteFile(),但这在方法和数据类型方面与 POSIX 的 open()/read()/write()/close()/etc 完全不同用过。

问题:在敲了敲键盘和几本教科书之后,我决定对你们所有人进行投票,看看:你们是如何完成跨操作系统文件的支持大文件的 I/O?

附:我有研究链接:

http://msdn.microsoft.com/en-us/library/windows/desktop/bb540534(v=vs.85).aspx Portable way to get file size in C/C++ How can I portably turn on large file support? http://mingw-users.1079350.n2.nabble.com/not-obvious-how-to-compile-programs-for-large-files-td5699144.html http://www.viva64.com/en/l/full/ https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/64bitPorting/HighLevelAPIs/HighLevelAPIs.html https://www.securecoding.cert.org/confluence/display/c/FIO19-C.+Do+not+use+fseek%28%29+and+ftell%28%29+to+compute+the+size+of+a+regular+file https://www.securecoding.cert.org/confluence/display/c/FIO03-C.+Do+not+make+assumptions+about+fopen%28%29+and+file+creation https://en.wikipedia.org/wiki/Large_file_support

【问题讨论】:

【参考方案1】:

看来,你需要一个不同版本的mingw:

http://mingw-w64.sourceforge.net/

w64 变体甚至在 32b 窗口上也支持 linux 兼容的大文件。

【讨论】:

【参考方案2】:

尽管我们都喜欢讨厌 M$ 的糟糕标准一致性,但这实际上是 ISO C 委员会的错。最初他们将 size_t 用于所有文件参数,但 size_t 是基于 ALU/内存架构而不是基于 OS 文件处理能力选择的。当每个人都切换到 64 位 CPU 时,MS 坚持使用 32 位长度,他们完全可以这样做并且仍然符合要求,但现在他们的文件比他们最大的算术类型大。

请注意,这最终在 C99 中得到解决,但 MSVC C99 支持基本上不存在。

然而,在内部,它们确实使用 64 位指针来跟踪您在文件中的位置。问题是由于不幸的 cstdlib API,您不能对大于 32 位的任何内容使用“fseek”或“ftell”。

为了证明 Windows 确实使用了 64 位文件指针,这段代码在使用 MSVC++ 编译时实际上会按预期工作,并会在您的硬盘驱动器上生成一个 40GB 的文件(无符号长整数为 32 位)。

#include <stdio.h>

int main(int argc, char **argv) 
    FILE *my_file;
    unsigned long i, j;

    my_file = fopen("bigfile.bin", "wb");

    for(i = 0; i < 10; i++) 
        for(j = 0; j < (1024 * 1024 * 1024); j++) 
            fwrite(&j, sizeof(j), 1, my_file);
        
    

    fclose(my_file);

    return 0;

那么这对您有什么帮助?嗯,MS 提供了自己的非标准 API,允许 64 位 fseek() 和 ftell()

https://msdn.microsoft.com/en-us/library/75yw9bf3.aspx

不过,您实际上可以使用常规 fseek() 以增量方式移动文件指针...基本上如果您这样做:

fseek(my_file, 0, SEEK_SET);
for(i = 0; i < 10; i++) 
    fseek(my_file, (1024 * 1024 * 1024), SEEK_CUR);

它将有效地将文件指针移动到 10GB 标记。

虽然使用 ftell(),但如果不使用 MS API,您可能会被搞砸。

TL;DR - fopen()、fread() 和 fwrite() 可以在大于 2GB 的大文件的 MSVC 上工作,但 ftell() 和 fseek() 不能,因为 API 设计不正确。

【讨论】:

【参考方案3】:

你不是完全没有Windows中的选项:

_fseeki64 _ftelli64

在 Linux 中,您可以使用 -D_FILE_OFFSET_BITS=64#define _FILE_OFFSET_BITS 64 可能工作,不确定)和 fseeko / ftello。许多系统还具有fseeko64ftello64),无论#define如何,它们都可以正常工作。

【讨论】:

以上是关于如何:C 中的跨操作系统大文件 IO?的主要内容,如果未能解决你的问题,请参考以下文章

Socket.IO 中的跨域连接

如何实现虚拟化环境下的跨网文件交换?

使用IO流写文件的一些骚操作

基础 IO(细节感拉满)

基础 IO(细节感拉满)

[OS-Linux]详解Linux的基础IO ------- 文件描述符fd