32-битная Windows и ограничение размера файла 2 ГБ (C с fseek и ftell)

Я пытаюсь перенести небольшую программу анализа данных из 64-разрядной UNIX в 32-разрядную систему Windows XP (не спрашивайте:)). Но теперь у меня проблемы с ограничением размера файла 2 ГБ (длинный не 64 бит на этой платформе).

Я искал этот сайт и другие для возможных решений, но не могу найти то, что напрямую переводится на мою проблему. Проблема заключается в использовании fseek и ftell.

Кто-нибудь знает о модификации следующих двух функций, чтобы заставить их работать с 32-разрядной Windows XP для файлов размером более 2 ГБ (на самом деле порядка 100 ГБ).

Очень важно, чтобы возвращаемый тип 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);
}

Я попытался использовать _fseeki64 и _ftelli64, используя следующее, чтобы заменить nsamples:

__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), что странно.

С любопытством, когда я оставляю приказ (__int64) при вызове _fseeki64, я получаю ошибку времени выполнения (недопустимый аргумент).

Любые идеи?

Ответ 1

извините за то, что вы не опубликовали раньше, но я некоторое время занимался другими проектами. Работает следующее решение:

__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, чтобы открыть файл. Я до сих пор не понимаю, почему это нужно сделать, но, по крайней мере, сейчас это работает. Спасибо всем за ваши предложения, которые в конце концов указали мне в правильном направлении.

Ответ 2

Есть две функции, называемые _fseeki64 и _ftelli64, которые поддерживают более длинные смещения файлов даже на 32-битной Windows:

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

__int64 _ftelli64(FILE *stream);

Ответ 3

И для gcc см. вопрос SO 1035657. Где совет компилируется с флагом -D_FILE_OFFSET_BITS = 64, так что скрытые переменные (типа off_t), используемые функциями f-move-around, являются 64-битными.

Для MinGW: "Поддержка больших файлов (LFS) реализована путем переопределения статистики и поиска функций и типов для их 64-битных эквивалентов. Для fseek и ftell отдельные версии LFS, fseeko и ftello, основанные на fsetpos и fgetpos, представлены в LibGw32C." (ссылка). В последних версиях gcc, fseeko и ftello встроены, и отдельная библиотека не нужна.

Ответ 4

Мой БК говорит:

520093696 + 4294967296 = > 4815060992

Я предполагаю, что ваша программа печати 32-разрядная. Ваше смещение вернулось, скорее всего, правильно, но где-то отрублено.