Неожиданный выходной файл копирования в C - PullRequest
2 голосов
/ 24 января 2009

В другом вопросе принятый ответ показывает метод чтения содержимого файла в память.

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

inputFile = fopen("D:\\input.txt", "r");
outputFile = fopen("D:\\output.txt", "w");

if(inputFile)
{
    //Get size of inputFile
    fseek(inputFile, 0, SEEK_END);
    inputFileLength = ftell(inputFile);
    fseek(inputFile, 0, SEEK_SET);

    //Allocate memory for inputBuffer   
    inputBuffer = malloc(inputFileLength);

    if(inputBuffer)
    {
        fread (inputBuffer, 1, inputFileLength, inputFile);
    }

    fclose(inputFile);

    if(inputBuffer)
    {
        fprintf(outputFile, "%s", inputBuffer);
    }

    //Cleanup
    free(inputBuffer);
    fclose(outputFile);
}

Выходной файл всегда содержит точную копию входного файла, но затем в конце добавляется текст «MPUTERNAM2». Кто-нибудь может пролить свет на то, почему это может происходить?

Ответы [ 8 ]

7 голосов
/ 24 января 2009

Вы можете быть счастливее с

int numBytesRead = 0;
if(inputBuffer)
{
  numBytesRead = fread (inputBuffer, 1, inputFileLength, inputFile);
}

fclose(inputFile);

if(inputBuffer)
{
  fwrite( inputBuffer, 1, numBytesRead, outputFile );
}

Для него не требуется строка с нулевым символом в конце (и, следовательно, он будет правильно работать с двоичными данными, содержащими нули)

4 голосов
/ 24 января 2009

Потому что вы пишете буфер как строку. Строки заканчиваются на NULL, файл, который вы читаете, не имеет значения.

Вы можете завершить строку NULL, но лучшим решением будет использование fwrite () вместо fprintf (). Это также позволит вам копировать файлы, содержащие символы NULL.

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

4 голосов
/ 24 января 2009

Вы не выделили достаточно места для завершающего нулевого символа в вашем буфере (и вы также забыли фактически установить его), так что ваш fprintf эффективно перегружается в какую-то другую память. Размер вашего буфера точно такой же, как и у файла, и он заполнен его содержимым, однако fprintf читает параметр, ищущий завершающий ноль, которого там нет, до тех пор, пока пара символов не появится, где, по совпадению, есть один.

EDIT
Вы фактически смешиваете два типа io: fread (который связан с fwrite) и fprintf (который связан с fscanf). Вы, вероятно, должны делать fwrite с количеством записываемых байтов; или, наоборот, используйте fscanf, который завершит вашу строку нулем (хотя это не допустит нулевых значений в вашей строке).

2 голосов
/ 24 января 2009

Распределение памяти под файл на самом деле довольно плохой способ сделать это, особенно то, как это делается здесь. Если malloc () завершается ошибкой, данные не записываются в выходной файл (и это происходит молча). Другими словами, вы не можете копировать файлы размером более нескольких гигабайт на 32-битной платформе из-за ограничений адресного пространства.

На самом деле гораздо лучше использовать меньший фрагмент памяти (выделенный или в стеке) и читать / записывать файл в виде фрагментов. Чтение и запись в любом случае будут буферизованы, и, пока вы делаете куски относительно большими, накладные расходы на вызовы функций для библиотек времени выполнения C минимальны.

Вы также должны всегда копировать файлы в двоичном режиме, это быстрее, так как нет шансов на перевод.

Что-то вроде:

FILE *fin = fopen ("infile","rb");  // make sure you check these for NULL return
FILE *fout = fopen ("outfile","wb");
char buff[1000000];  // or malloc/check-null if you don't have much stack space.
while ((count = fread (buff, 1, sizeof(buff), fin)) > 0) {
    // Check count == -1 and errno here.
    fwrite (buff, 1, count, fout); // and check return value.
}
fclose (fout);
fclose (fin);

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

1 голос
/ 24 января 2009

Вы можете использовать

fwrite (inputBuffer , 1 , inputFileLength , outputFile );

вместо fprintf, чтобы избежать проблемы строки с нулевым символом в конце. Это также "лучше соответствует" с fread:)

1 голос
/ 24 января 2009

В дополнение к тому, что сказали другие: вы также должны открывать свои файлы в двоичном режиме - в противном случае вы можете получить неожиданные результаты в Windows (или других системах, отличных от POSIX).

1 голос
/ 24 января 2009

fprintf ожидает, что inputBuffer завершится нулем, а это не так. Поэтому он читает после конца inputBuffer и печатает все, что там есть (в ваш новый файл), пока не найдет нулевой символ.

В этом случае вы можете назначить лишний байт и поставить ноль в качестве последнего символа в inputBuffer.

0 голосов
/ 24 января 2009

Попробуйте вместо этого использовать fgets, в конце строки для вас добавится ноль. Также, как было сказано выше, вам нужен еще один пробел для нулевого терминатора.

е

Строка "Davy" представляется в виде массива, содержащего D, a, v, y, \ 0 (без запятых). По сути, ваш массив должен иметь как минимум sizeofstring + 1, чтобы содержать нулевой терминатор. Кроме того, fread не будет автоматически добавлять терминатор, поэтому, даже если ваш файл намного короче максимальной длины, вы получаете мусор.

Обратите внимание, что альтернативный способ быть ленивым - просто использовать calloc, который устанавливает строку в 0. Но все же вам следует только перечитывать только символы inputFileLength-1.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...