32位Windows和2GB文件大小限制(使用fseek和ftell的C语言)

6
我正试图将一个小型数据分析程序从64位UNIX移植到32位Windows XP系统中(别问我为什么)。 但现在我遇到了2GB文件大小限制的问题(因为该平台上的long不是64位)。
我已经搜索了这个网站和其他网站,寻找可能解决我的问题的方法,但没有找到任何直接适用于我的问题的方法。 问题出现在fseek和ftell函数的使用上。
是否有人知道如何修改以下两个函数,使它们能够处理大于2GB(实际上为约100GB)的文件,且nsamples的返回类型必须是64位整数(可能是int64_t)。
long nsamples(char* filename)
{
  FILE *fp;
  long n;

  /* Open file */
  fp = fopen(filename, "rb");

  /* Find end of file */
  fseek(fp, 0L, SEEK_END);

  /* Get number of samples */
  n = ftell(fp) / sizeof(short);

  /* Close file */
  fclose(fp);

  /* Return number of samples in file */
  return n;
}

并且

void readdata(char* filename, short* data, long start, int n)
{
  FILE *fp;

  /* Open file */
  fp = fopen(filename, "rb");

  /* Skip to correct position */
  fseek(fp, start * sizeof(short), SEEK_SET);

  /* Read data */
  fread(data, sizeof(short), n, fp);

  /* Close file */
  fclose(fp);
}

我尝试使用以下代码替换nsamples来使用_fseeki64和_ftelli64:

__int64 nsamples(char* filename)
{
  FILE *fp;
  __int64 n;
  int result;

  /* Open file */
  fp = fopen(filename, "rb");
  if (fp == NULL)
  {
    perror("Error: could not open file!\n");
    return -1;
  }

  /* Find end of file */
  result = _fseeki64(fp, (__int64)0, SEEK_END);
  if (result)
  {
    perror("Error: fseek failed!\n");
    return result;
  }

  /* Get number of samples */
  n = _ftelli64(fp) / sizeof(short);

  printf("%I64d\n", n);

  /* Close file */
  fclose(fp);

  /* Return number of samples in file */
  return n;
}

对于一个大小为4815060992字节的文件,我得到了260046848个样本(例如_ftelli64给出的是520093696字节),这很奇怪。

有趣的是,当我在调用_fseeki64时省略了(__int64)转换时,我会得到运行时错误(无效参数)。

有什么想法吗?


你使用的编译器是什么?gcc?Visual(某个版本)?还是其他的? - Eric Towers
我正在使用MinGW("不能"使用VS,因为我编写的函数是f2py Python扩展模块的一部分)。如果Win32 API可以轻松集成到此函数中而不会添加太多依赖项,则可能是一个选项(正如您可能已经注意到的那样,我对Windows并不是很熟悉:))。 - Pim Schellart
我也发布了一个更具体的问题,如果那个问题得到解答,我会在这里添加最终的解决方案。 - Pim Schellart
4个回答

5

抱歉没有及时发布文章,我一段时间以来一直忙于其他项目。以下解决方案可行:

__int64 nsamples(char* filename)
{
  int fh;
  __int64 n;

  /* Open file */
  fh = _open( filename, _O_BINARY );

  /* Find end of file */
  n = _lseeki64(fh, 0, SEEK_END);

  /* Close file */
  _close(fh);

 return n / sizeof(short);
}

这个技巧是使用_open而不是fopen来打开文件。 我仍然不完全理解为什么必须这样做,但至少现在它可以工作了。 感谢大家的建议,最终让我找到了正确的方向。

3

有两个函数叫做_fseeki64和_ftelli64,即使在32位Windows上也支持更长的文件偏移量:

int _fseeki64(FILE *stream, __int64 offset, int origin);

__int64 _ftelli64(FILE *stream);

我尝试了这个,但似乎没有返回正确的值(请参见帖子) - Pim Schellart
你使用哪个编译器?VisualStudio吗?还是哪个版本? - Codo
我使用最新版本的MinGW(基本上是GCC 4.5)。由于我正在编译一个带有f2py的Python扩展包,而我不知道如何使用VisualStudio进行编译。 - Pim Schellart
我从未使用过MinGW,所以恐怕无法提供真正的帮助。但是,您是否看过类Unix库中可用的_ftello_和_fseeko_,它们是_ftell_和_fseek_的64位版本? - Codo
我已经查看了这些(fseeko _ftello)函数,但不确定它们是否也适用于Windows(在我的64位UNIX机器上没有问题,因为我可以使用带有64位长整型的fseek和ftell,这确实是一个Windows特定的问题)。 - Pim Schellart

1

我的BC说:

520093696 + 4294967296 => 4815060992

我猜测你的打印程序是32位的。你返回的偏移量很可能是正确的,但在某个地方被裁剪了。


1

对于gcc,请参见SO问题1035657。建议使用标志-D_FILE_OFFSET_BITS=64进行编译,以便f-move-around函数使用的隐藏变量(类型为off_t)是64位。

对于MinGW:“通过重新定义stat和seek函数和类型为它们的64位等效项来实现了大文件支持(LFS)。对于fseek和ftell,提供了基于fsetpos和fgetpos的单独LFS版本fseeko和ftello,在LibGw32C中提供。”(reference)。在最新版本的gcc中,fseeko和ftello是内置的,不需要单独的库。


@Pim Schellart:我既不能确认也不能否认。我的两个当前正在工作的gcc设置是Linux/POSIX(根据您的问题进行上下文)。在这些设置上进行测试,我从fseek()和ftell()中看到了LFS行为。因此,我无法在非POSIX环境中使用gcc进行测试。 - Eric Towers
fseeko和ftello已经随最新的MinGW gcc 4.8.2提供;您不需要LibGw32C。 - JPaget

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接