Может открыть небольшой файл ASCII, но не большой двоичный файл?

Я использую приведенный ниже код, чтобы открыть большой (5.1GB) двоичный файл в MSVC в Windows. Машина имеет много оперативной памяти. Проблема заключается в том, что длина восстанавливается как ноль. Однако, когда я изменяю путь file_path к меньшему файлу ASCII, код работает нормально.

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

FILE * pFile;
uint64_t lSize;
char * buffer;
size_t result;

pFile = fopen(file_path, "rb");
if (pFile == NULL) { 
    fputs("File error", stderr); exit(1); 
}

// obtain file size:
fseek(pFile, 0, SEEK_END);
lSize = ftell(pFile);                                // RETURNS ZERO
rewind(pFile);

// allocate memory to contain the whole file:
buffer = (char*)malloc(sizeof(char)*lSize);
if (buffer == NULL) {
    fputs("Memory error", stderr); exit(2); 
}

// copy the file into the buffer:
result = fread(buffer, 1, lSize, pFile);             // RETURNS ZERO TOO
if (result != lSize) {                               // THIS FAILS
    fputs("Reading error", stderr); exit(3); 
}

/* the whole file is now loaded in the memory buffer. */

это не разрешения файла или что-то еще, они в порядке.

Ответ 1

Если вы выделяете 5,1 ГБ, вам лучше убедиться, что вы скомпилировали свой код в 64 бита и запустили его в 64-битной версии Windows. Ohterwhise, память адресное пространство ограничено до max 3 ГБ на 32 битах Windows и 4 ГБ с 32-битным кодом на 64-битной Windows.

Кстати, ftell() возвращает подписанный long. Вы должны проверить, что здесь нет ошибки (например, переполнение, если ОС позволяет увеличить размер файла), так что значение не равно -1.

Изменить:

Обратите внимание, что с MSVC, long будет в настоящее время содержать 32-битное число, даже если скомпилировано для 64 бит. Это означает, что ftell() даст вам значимый результат, если размер файла ниже 2 ГБ (потому что для знака).

Для получения размера больших файлов в подписанном 64-битовом номере вы можете использовать неконсервативную ОС WinAPI GetFileSizeEx().

malloc() принимает size_t, который является unsigned 64-разрядным номером. Так что с этой стороны вы в безопасности.

Альтернативой будет использование сопоставление файлов.

Второе редактирование

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

Рассматривая эту рекомендацию по безопасности CERT, оказалось, что гарантии, предлагаемые стандартом для fseek() в сочетании с SEEK_END, являются недостаточными и сделать это очень опасным подходом.

Итак, пусть повторяется: самым простым способом получить размер будет использование встроенной функции OS i.e. GetFileSizeEx() в Windows. Там обходной путь для 64-битных окон: используйте _fseeki64() и _ftelli64():

...
if (_fseeki64(pFile, 0, SEEK_END)) {
    fputs("File seek error", stderr); 
    return (1);
}
lSize = _ftelli64(pFile);                            // RETURNS EXACT SIZE
...

Это работало очень хорошо (исходная проблема, казалось, была связана с типом возвращаемого значения, который был недостаточно большим). Однако имейте в виду, что это обходной путь, и я боюсь, что могут быть другие ошибки, которые могут привести к уязвимости, о которой сообщает CERT.

Ответ 2

Тип данных long слишком мал, чтобы представлять размер файла. Используйте метод stat() (или альтернативу GetFileAttributes, чтобы прочитать размер файла.