Есть несколько проблем в вашей функции, как написано.
fopen(dstFilename, "w+b");
обрезает файл, поэтому ваша проверка на перезапись позже не имеет смысла. - Вы не проверяете на NULLпосле malloc, и ваш буфер должен быть
unsigned char*
, поскольку fread/fwrite
будет интерпретировать его как. - В конце обе функции
fclose
могут быть вызваны с указателями файлов NULL, что может привести кавария.Вы должны переместить их в области, где вы знаете, что каждый был успешно открыт. - Большая проблема, которая вызвала этот вопрос, заключается в том, что вы не обрабатываете случаи, когда размер файла не является даже кратнымвсего, что есть
BYTE_SIZE
Поскольку вы выделили достаточно памяти для всего файла, вы должны просто прочитать и записать весь файл.fread(buf, 1, size, src);
и fwrite(buf, 1, size, dst);
.В общем случае лучше всего сделать параметр размера элемента равным fread/fwrite
1 и подсчитать количество байтов, которое вы хотите прочитать или записать.Там нет математики, чтобы пойти не так, и вы можете точно сказать, сколько байтов было прочитано / записано.
Вот версия вашей исходной функции, которую я исправил и аннотировал, так что она работает, если ничего не идет не так, как надо.
void originalBinaryCopy(const char *srcFilename, const char *dstFilename)
{
//odd size to ensure remainder
const size_t BYTE_SIZE = 777;
int *buf = 0;
int elements = 0;
int size = 0, wantOverwrite = 0;
FILE *src = fopen(srcFilename, "rb");
//This truncates dst, so the overwirte check is meaningless
FILE *dst = fopen(dstFilename, "w+b");
if (src)
{
if (dst)
{
fseek(src, 0L, SEEK_END);
size = ftell(src);
fseek(src, 0L, SEEK_SET);
//always check for NULL after malloc - This should be a char*
buf = (int *)malloc(size);
if (!buf)
{
fclose(dst);
fclose(src);
return;
}
elements = fread(buf, BYTE_SIZE, size / BYTE_SIZE, src);
fwrite(buf, BYTE_SIZE, elements, dst);
//added, copy remainder
elements = fread(buf, 1, size % BYTE_SIZE, src);
fwrite(buf, 1, size % BYTE_SIZE, dst);
//end
printf("copy completed %s -> %s\n", srcFilename, dstFilename);
free(buf);
}
}
//dst could be NULL here, move inside if(dst) scope above
fclose(dst);
//src could be NULL here, move inside if(src) scope above
fclose(src);
if (comp(srcFilename, dstFilename) != 0)
{
printf("compare failed - %s -> %s\n", srcFilename, dstFilename);
}
}
Обратите внимание, как обрабатывается остаток в конце.
Вот как я буду обрабатывать копирование файлов вместе с набором тестов для создания, копирования и проверки набора файлов.,Он показывает, как избежать усечения места назначения, если вы этого не хотите, и имеет небольшую проверку ошибок в реальных функциях.Я не включил какую-либо конкретную проверку ошибок на стороне вызывающей стороны, но для реального кода я бы перечислил все возможные ошибки и использовал бы эти возвращаемые значения для передачи в функцию обработки ошибок, которая могла бы их распечатать и, возможно, выйти из программы.
Управление файлами - это одна вещь, к которой вы хотите ОЧЕНЬ проявлять осторожность, поскольку существует вероятность потери данных, если ваш код не работает, поэтому прежде чем использовать его с реальнымфайлы должны быть на 100% твердыми с тестовыми файлами.
#include <malloc.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define TEST_FILE_MIN 1024
#define TEST_FILE_MAX 1024 * 1024
const char *src_pattern = "src_file_%08x.bin";
const char *dst_pattern = "dst_file_%08x.bin";
void createTestFiles(const char *pattern)
{
char filename[256] = { 0 };
char buffer[1024];
for (size_t i = 0; i < sizeof(buffer); ++i)
{
buffer[i] = rand();
}
for (size_t i = TEST_FILE_MIN; i <= TEST_FILE_MAX; i *= 2)
{
sprintf(filename, pattern, i);
FILE *dst = fopen(filename, "wb");
if (dst)
{
size_t reps = i / TEST_FILE_MIN;
for (size_t w = 0; w < reps; ++w)
{
fwrite(buffer, 1, sizeof(buffer), dst);
}
fclose(dst);
}
}
}
int comp(const char *srcFilename, const char *dstFilename)
{
FILE *src = fopen(srcFilename, "rb");
if (!src)
{
return -1;
}
//open for reading to check for existence
FILE *dst = fopen(dstFilename, "rb");
if (!dst)
{
fclose(src);
return -2;
}
fseek(src, 0, SEEK_END);
size_t srcSize = ftell(src);
fseek(src, 0, SEEK_SET);
fseek(dst, 0, SEEK_END);
size_t dstSize = ftell(dst);
fseek(dst, 0, SEEK_SET);
if (srcSize == 0 || dstSize == 0 || srcSize != dstSize)
{
fclose(src);
fclose(dst);
return -3;
}
unsigned char *srcBuf = (unsigned char *)calloc(1, srcSize);
unsigned char *dstBuf = (unsigned char *)calloc(1, srcSize);
if (!srcBuf || !dstBuf)
{
fclose(src);
fclose(dst);
return -4;
}
if (fread(srcBuf, 1, srcSize, src) != srcSize)
{
fclose(src);
fclose(dst);
return -5;
}
if (fread(dstBuf, 1, dstSize, dst) != dstSize)
{
fclose(src);
fclose(dst);
return -6;
}
fclose(src);
fclose(dst);
//result * 100 to make this error outside te range of the other general errors from this function.
int result = memcmp(srcBuf, dstBuf, srcSize) * 100;
free(srcBuf);
free(dstBuf);
return result;
}
void originalBinaryCopy(const char *srcFilename, const char *dstFilename)
{
//odd size to ensure remainder
const size_t BYTE_SIZE = 777;
int *buf = 0;
int elements = 0;
int size = 0, wantOverwrite = 0;
FILE *src = fopen(srcFilename, "rb");
//This truncates dst, so the overwirte check is meaningless
FILE *dst = fopen(dstFilename, "w+b");
if (src)
{
if (dst)
{
fseek(src, 0L, SEEK_END);
size = ftell(src);
fseek(src, 0L, SEEK_SET);
//always check for NULL after malloc - This should be a char*
buf = (int *)malloc(size);
if (!buf)
{
fclose(dst);
fclose(src);
return;
}
elements = fread(buf, BYTE_SIZE, size / BYTE_SIZE, src);
fwrite(buf, BYTE_SIZE, elements, dst);
//added, copy remainder
elements = fread(buf, 1, size % BYTE_SIZE, src);
fwrite(buf, 1, size % BYTE_SIZE, dst);
//end
printf("copy completed %s -> %s\n", srcFilename, dstFilename);
free(buf);
}
}
//dst could be NULL here, move inside if(dst) scope above
fclose(dst);
//src could be NULL here, move inside if(src) scope above
fclose(src);
if (comp(srcFilename, dstFilename) != 0)
{
printf("compare failed - %s -> %s\n", srcFilename, dstFilename);
}
}
int binaryCopy(const char *srcFilename, const char *dstFilename, bool overwrite)
{
//arbitrary odd size so we can make sure we handle a partial buffer.
//assuming the code tests successfully I'd use something like 64 * 1024.
unsigned char buffer[7777] = { 0 };
FILE *src = fopen(srcFilename, "rb");
if (!src)
{
//Error, source file could not be opened
return -1;
}
//open for reading to check for existence
FILE *dst = fopen(dstFilename, "rb");
if (dst)
{
if (!overwrite)
{
//Error, dest file exists and we can't overwrite it
fclose(src);
fclose(dst);
return -2;
}
//reopen dst it for writing
if (!freopen(dstFilename, "wb", dst))
{
fclose(src);
fclose(dst);
dst = NULL;
}
}
else
{
//it didn't exist, create it.
dst = fopen(dstFilename, "wb");
}
if (!dst)
{
//Error, dest file couldn't be opened
fclose(src);
return -3;
}
//Get the size of the source file for comparison with what we read and write.
fseek(src, 0, SEEK_END);
size_t srcSize = ftell(src);
fseek(src, 0, SEEK_SET);
size_t totalRead = 0;
size_t totalWritten = 0;
size_t bytesRead = 0;
while (bytesRead = fread(buffer, 1, sizeof(buffer), src))
{
totalRead += bytesRead;
totalWritten += fwrite(buffer, 1, bytesRead, dst);
}
fclose(dst);
fclose(src);
if (totalRead != srcSize)
{
//src read error
return -4;
}
if (totalWritten != srcSize)
{
//dst write error
return -5;
}
return 0;
}
int main()
{
srand((unsigned)time(0));
createTestFiles(src_pattern);
for (size_t i = TEST_FILE_MIN; i <= TEST_FILE_MAX; i *= 2)
{
char srcName[256];
char dstName[256];
sprintf(srcName, src_pattern, i);
sprintf(dstName, dst_pattern, i);
//use my copy to create dest file
if (binaryCopy(srcName, dstName, true) != 0)
{
printf("File: '%s' failed initial copy.", srcName);
}
originalBinaryCopy(srcName, dstName);
if (binaryCopy(srcName, dstName, true) != 0)
{
printf("File: '%s' failed overwrite copy.", srcName);
}
if (binaryCopy(srcName, dstName, false) == 0)
{
printf("File: '%s' succeeded when file exists and overwrite was not set.", srcName);
}
//If compare succeeds delete the files, otherwise leave them for external comparison and print an error.
if (comp(srcName, dstName) == 0)
{
if (remove(srcName) != 0)
{
perror("Could not remove src.");
}
if (remove(dstName) != 0)
{
perror("Could not remove dst.");
}
}
else
{
printf("File: '%s' did not compare equal to '%s'.", srcName, dstName);
}
}
return 0;
}
Надеюсь, это даст вам возможность поэкспериментировать, чтобы убедиться, что ваш копир настолько хорош, насколько это возможно.Также стоит отметить, я бы не стал различать копирование текстовых / бинарных файлов.Файлы - это файлы, и если ваша цель - скопировать их, вы всегда должны делать это в двоичном режиме, чтобы копия была идентичной.В операционных системах, отличных от Windows, это не имеет значения, но в Windows есть ряд ловушек, с которыми вы можете столкнуться в текстовом режиме.Лучше всего избегать их, если сможешь.
Удачи!