Имея буфер такого размера, как файл не является экономичным для больших значений буфера («большой» в зависимости от платформы и операционной системы, но я сомневаюсь, что он превысит, скажем, один мегабайт, прежде чем нанести убытки). В некоторых системах вам может быть разрешено выделять гораздо больше, чем физическая доступная память, буфер подкрепляется областью подкачки на диске. На этом этапе, если вы попытаетесь скопировать весь файл одним махом, вы можете в конечном итоге прочитать и записать большую часть файла в область подкачки, а затем вернуться из области подкачки к новому файлу, эффективно удвоить (в наименьшее) время копирования.
Так что я бы использовал петлю.
Вам также необходимо проверить ошибки в распределении памяти и записи файлов, а также учитывать, что размер int
может вызвать проблемы с большими файлами (в настоящее время размер доступного файла составляет 2 ГБ, однако он переполнит 32-разрядную подпись целое число).
// Copies a part of a file from source to destination
// and returns number of bytes written.
// if input size is < 0, copies the whole file.
ssize_t copy_file(char* source, char* destination, int size)
{
if ((source == NULL) || (destination == NULL) || (access(source, F_OK) == -1)) {
return 0;
}
#define BUFFER_SIZE 1048576
char* buffer = malloc(BUFFER_SIZE);
if (NULL == buffer) {
return 0;
}
int fd_from = open(source, O_RDONLY);
if (fd_from < 0) {
free(buffer);
return 0;
}
int fd_to = open(destination, O_WRONLY | O_CREAT | O_TRUNC, 0777);
if (fd_to < 0) {
free(buffer);
// Avoid leaking a file handle in case of error.
close(fd_from);
return 0;
}
ssize_t written = 0;
// This checks that size is != 0.
// As a result, passing a size < 0 will copy the whole source,
// whatever its length.
// The condition is written explicitly, deliberately (a simple
// while(size) might be overlooked or mistaken for a bug).
while((size > 0)||(size < 0)) {
int ch_r;
ch_r = read(fd_from, buffer, BUFFER_SIZE);
if (ch_r) {
if (ch_r != write(fd_to, buffer, ch_r)) {
// Out of storage space?
close(fd_from);
close(fd_to);
free(buffer);
unlink(destination);
return 0;
}
} else {
// finished
break;
}
written += ch_r;
// We do have a problem of integer size. if
// sizeof(int) is 4 (32bit), files or sizes larger than 2 GB will
// likely misbehave.
size -= ch_r;
}
close(fd_to);
close(fd_from);
free(buffer);
return written;
}
Кроме того, вы можете найти его полезным, чтобы вернуть статус ошибки вместо размера. Если вы возвращаете ноль, то вы знаете, что количество записанных байтов равно размеру ввода. Если вам необходимо вернуть оба значения, вы можете поместить ошибку в переменной передается по указателю:
ssize_t copy_file(char* source, char* destination, int size, int *status)
{
*status = 0; // Begin with "no error"
...
if (NULL == buffer) {
*status = -8; // -8 stands for "out of memory"
return 0;
}
...
Таким образом, в случае ошибки, вы будете знать, почему процедура возвращается ноль.Также вы сможете создавать файлы нулевой длины в случае необходимости (функция вернет 0, но статус также будет 0, что указывает на то, что запись нулевых байтов не является ошибкой).
Чтобы скопировать обычный файл, при этом нет необходимости указывать размер файла:
// Copies a file from source to destination
// and returns number of bytes written.
ssize_t copy_file(char* source, char* destination)
{
if ((source == NULL) || (destination == NULL) || (access(source, F_OK) == -1)) {
return 0;
}
#define BUFFER_SIZE 1048576
char* buffer = malloc(BUFFER_SIZE);
if (NULL == buffer) {
return 0;
}
int fd_from = open(source, O_RDONLY);
if (fd_from < 0) {
free(buffer);
return 0;
}
int fd_to = open(destination, O_WRONLY | O_CREAT | O_TRUNC, 0777);
if (fd_to < 0) {
free(buffer);
// Avoid leaking a file handle in case of error.
close(fd_from);
return 0;
}
ssize_t written = 0;
// Infinite loop, exiting when nothing more can be read
for(;;) {
int ch_r;
ch_r = read(fd_from, buffer, BUFFER_SIZE);
if (ch_r) {
if (ch_r != write(fd_to, buffer, ch_r)) {
// Out of storage space?
close(fd_from);
close(fd_to);
free(buffer);
unlink(destination);
return 0;
}
} else {
// finished
break;
}
written += ch_r;
}
close(fd_to);
close(fd_from);
free(buffer);
return written;
}
, что вы имеете в виду, что это работает? –
Иногда он не создает файл назначения вообще. Иногда это происходит, но размер файла равен 0 байтам. И ~ 10% времени он работает правильно. – Ryan
Любая причина, по которой вы используете функции POSIX вместо стандартных функций библиотеки C? –