【问题标题】:fwrite() alternative for large files on 32-bit systemfwrite() 替代 32 位系统上的大文件
【发布时间】:2013-05-13 10:26:42
【问题描述】:

我正在尝试使用 C 代码生成大文件 (4-8 GB)。 现在我使用fopen()'wb' 参数打开文件二进制文件,并在for 循环中使用fwrite() 函数将字节写入文件。我在每次循环迭代中写一个字节。在文件大于或等于 4294967296 字节 (4096 MB) 之前没有问题。它看起来像 32 位操作系统中的一些内存限制,因为当它写入打开的文件时,它仍然在 RAM 中。我对吗?症状是创建的文件比我想要的要小。差异为 4096 MB,例如当我想要 6000 MB 的文件时,它会创建 6000 MB - 4096 MB = 1904 MB 的文件。

您能建议其他方法来完成这项任务吗?

问候:)

部分代码:

unsigned long long int number_of_data = (unsigned int)atoi(argv[1])*1024*1024; //MB
char x[1]={atoi(argv[2])};

fp=fopen(strcat(argv[3],".bin"),"wb");

    for(i=0;i<number_of_data;i++) {
        fwrite(x, sizeof(x[0]), sizeof(x[0]), fp);
    }

fclose(fp);

【问题讨论】:

  • strcat(argv[3],".bin")
  • 为什么投反对票?这是一个很好的问题。
  • 无法保证可以绑定到“.bin”的区域已准备好。它可能已经破坏了程序。
  • 你使用哪个操作系统和哪个文件系统?
  • @RaphaelAhrens 我使用的是 Windows 7 32 位和 NTFS 分区。

标签: c windows file-io


【解决方案1】:

fwrite 不是这里的问题。问题是您为number_of_data 计算的值。

在处理 64 位整数时,您需要小心任何无意的 32 位转换。当我定义它们时,我通常分几个独立的步骤来做,每一步都要小心:

unsigned long long int number_of_data = atoi(argv[1]); // Should be good for up to 2,147,483,647 MB (2TB)
number_of_data *= 1024*1024; // Convert to MB

赋值运算符 (*=) 将作用于左值 (unsigned long long int),因此您可以相信它作用于 64 位值。

这可能看起来没有优化,但一个体面的编译器会删除任何不必要的步骤。

【讨论】:

  • 这个解决方案帮助了我 :) 那确实是那个变量值的问题。但是非常感谢所有回答这个问题的人。它非常有用且内容丰富。
【解决方案2】:

在 Windows 上创建大文件应该没有任何问题,但我注意到,如果您在文件上使用 32 位版本的 seek,那么它似乎会确定它是 32 位文件,因此不能大于 4GB。在 Windows 上处理 >4GB 文件时,我已经成功使用 _open、_lseeki64 和 _write。例如:

static void
create_file_simple(const TCHAR *filename, __int64 size)
{
    int omode = _O_WRONLY | _O_CREAT | _O_TRUNC;
    int fd = _topen(filename, omode, _S_IREAD | _S_IWRITE);
    _lseeki64(fd, size, SEEK_SET);
    _write(fd, "ABCD", 4);
    _close(fd);
}

以上将创建一个超过 4GB 的文件而不会出现问题。但是,它可能会很慢,因为当您在那里调用 _write() 时,文件系统必须实际为您分配磁盘块。如果您必须随机填充它,您可能会发现创建稀疏文件更快。如果您从头开始按顺序填充文件,那么上面的代码就可以了。请注意,如果您真的想使用 fwrite 提供的缓冲 IO,您可以使用 fdopen() 从 C 库文件描述符中获取 FILE*。

(如果有人想知道,TCHAR、_topen 和下划线前缀都是 MSVC++ 的怪癖)。

更新

最初的问题是使用 N 字节值 V 的顺序输出。所以一个应该实际生成所需文件的简单程序是:

#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <io.h>
#include <tchar.h>
int
_tmain(int argc, TCHAR *argv[])
{
    __int64 n = 0, r = 0, size = 0x100000000LL; /* 4GB */
    char v = 'A';
    int fd = _topen(argv[1], _O_WRONLY | _O_CREAT| _O_TRUNC, _S_IREAD | _S_IWRITE);
    while (r != -1 && n < count) {
        r = _write(fd, &v, sizeof(value));
        if (r >= 0) n += r;
    }
    _close(fd);
    return 0;
}

但是,这会真的很慢,因为我们一次只写入一个字节。这可以通过使用更大的缓冲区或通过在描述符 (fd) 上调用 fdopen 并切换到 fwrite 来使用缓冲 I/O 来改进。

【讨论】:

  • 您的 MSVC++ 怪癖在 MinGW(-w64) GCC 下也可以正常工作;它们是 Windows C 运行时库的怪癖,而不是编译器。
  • 可能这就是我正在寻找的东西,但我离“Hello World”并不远,现在我很难以我想要的方式使用它:D 请给我一会儿。
  • @patthoyts 你能告诉我如何为该函数指定文件名吗?
  • 类似这样的东西:TCHAR fname[] = "Name"; 不能使用...我不知道如何运行这个函数。
  • TCHAR 是 MS 定义的类型,根据定义的 UNICODE 编译为 char 或 wchar_t。它在 Windows C/C++ 代码中很常见。您将使用TCHAR fname[] = _T("Name"); 创建一个适合当前编译环境的字符数组。基本上在 Windows98 和更早版本上,TCHAR 是 char,但在 NT、XP 和更新的 TCHAR 上是 wchar_t(支持 unicode)。
【解决方案3】:

你对fwrite()没有问题。问题似乎是你的

unsigned long long int number_of_data = (unsigned int)atoi(argv[1])*1024*1024; //MB

确实应该是这样的

uint16_t number_of_data = atoll(argv[1])*1024ULL*1024ULL;

unsigned long long 仍然可以,但无论您的目标变量有多大,unsigned int * int * int 都会给您一个unsinged int

【讨论】:

  • 这就是我收到溢出警告的原因。但它现在告诉我atoll 未定义:/ 我有#include &lt; stdlib.h &gt;
  • 尝试 #include 并使用 _strtoui64(如果使用 TCHAR 类型,则使用 _tcstoui64)。
  • 我明天会尝试这个解决方案,但它可能会像@Lee Netherton 解决方案一样有所帮助(同样的问题问题)。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-08
  • 1970-01-01
  • 2012-02-04
  • 2011-05-04
  • 2015-02-11
  • 1970-01-01
  • 2014-09-28
相关资源
最近更新 更多