Альтернатива fwrite() для больших файлов на 32-битной системе

Я пытаюсь создать большие файлы (4-8 ГБ) с кодом C. Теперь я использую fopen() с параметрами 'wb', чтобы открыть двоичный файл файла и fwrite() в цикле for для записи байтов в файл. Я пишу один байт в каждой итерации цикла. Нет проблем, пока файл не станет больше или равен 4294967296 байт (4096 МБ). Это похоже на ограничение памяти в 32-битной ОС, потому что когда он записывает этот открытый файл, он все еще находится в ОЗУ. Я прав? Симптом заключается в том, что созданный файл имеет меньший размер, чем я хочу. Разница составляет 4096 МБ, например. когда я хочу 6000 МБ файла, он создает 6000 МБ - 4096 МБ = 1904 МБ файла.

Не могли бы вы предложить другой способ выполнить эту задачу?

С уважением:)

Часть кода:

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);

Ответ 1

fwrite здесь не проблема. Проблема - это значение, которое вы вычисляете для number_of_data.

Вы должны быть осторожны с любым непреднамеренным 32-битным литьем при работе с 64-битными целыми числами. Когда я их определяю, я обычно делаю это на нескольких дискретных шагах, осторожно на каждом шагу:

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

Оператор присваивания (*=) будет действовать на l-значение (unsigned long long int), поэтому вы можете доверять ему, чтобы он работал с 64-битным значением.

Это может выглядеть неоптимизированным, но достойный компилятор удалит все ненужные шаги.

Ответ 2

У вас не должно возникнуть проблем с созданием больших файлов в Windows, но я заметил, что если вы используете 32-битную версию поиска в файле, то, похоже, она решила, что это 32-битный файл и, следовательно, не может быть больше 4 ГБ. У меня был успех с использованием _open, _lseeki64 и _write при работе s > 4 ГБ файлами в Windows. Например:

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);
}

Вышеупомянутый файл создаст файл объемом более 4 ГБ без проблем. Тем не менее, это может быть медленным, так как при вызове _write() там файловая система должна фактически распределять блоки диска для вас. Вы можете быстрее найти небольшой разрешенный файл, если вам нужно его случайно заполнить. Если вы будете заполнять файл последовательно с самого начала, то указанный выше код будет в порядке. Обратите внимание: если вы действительно хотите использовать буферизованное IO, предоставленное fwrite, вы можете получить FILE * из дескриптора файла библиотеки C, используя fdopen().

(В случае, если кто-то задается вопросом, префиксы TCHAR, _topen и underscore - это все приличия MSVС++).

UPDATE

Исходный вопрос заключается в использовании последовательного вывода для 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;
}

Однако это будет очень медленным, поскольку мы пишем только один байт за раз. Это то, что можно улучшить, используя большой буфер или используя буферизованный ввод-вывод, вызывая fdopen на дескрипторе (fd) и переключаясь на fwrite.

Ответ 3

У Yuo нет проблем с 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 независимо от того, насколько велика ваша целевая переменная.