2015-04-02 2 views
0

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

// Copies the file from source to destination and returns number of bytes written 
ssize_t copy_file(char* source, char* destination, int size) 
{ 
    if (source == NULL || destination == NULL || access(source, F_OK) == -1) 
     return 0; 

    int fd_to = open(destination, O_WRONLY | O_CREAT | O_TRUNC, 0777); 
    int fd_from = open(source, O_RDONLY); 
    char* buffer = malloc(sizeof(size)); 
    ssize_t written; 

    if (fd_to < 0 | fd_from < 0) 
     return 0; 

    read(fd_from, buffer, size); 
    written = write(fd_to, buffer, size); 
    close(fd_to); 
    close(fd_from); 
    free(buffer); 

    return written; 
} 
+0

, что вы имеете в виду, что это работает? –

+0

Иногда он не создает файл назначения вообще. Иногда это происходит, но размер файла равен 0 байтам. И ~ 10% времени он работает правильно. – Ryan

+0

Любая причина, по которой вы используете функции POSIX вместо стандартных функций библиотеки C? –

ответ

2

Имея буфер такого размера, как файл не является экономичным для больших значений буфера («большой» в зависимости от платформы и операционной системы, но я сомневаюсь, что он превысит, скажем, один мегабайт, прежде чем нанести убытки). В некоторых системах вам может быть разрешено выделять гораздо больше, чем физическая доступная память, буфер подкрепляется областью подкачки на диске. На этом этапе, если вы попытаетесь скопировать весь файл одним махом, вы можете в конечном итоге прочитать и записать большую часть файла в область подкачки, а затем вернуться из области подкачки к новому файлу, эффективно удвоить (в наименьшее) время копирования.

Так что я бы использовал петлю.

Вам также необходимо проверить ошибки в распределении памяти и записи файлов, а также учитывать, что размер 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; 
} 
+1

вам больше не нужен размер, просто прочитайте до конца файла – pm100

+0

Я вижу. Я предположил, что OP хочет иметь возможность копировать только область в начале файла или конечный фрагмент из файла бесконечной длины (например,/dev/zero или/dev/urandom). Вот почему я добавил возможность указать размер -1 для полной копии файла неизвестной, но конечной длины. - * отредактированный ответ. Спасибо за хедз-ап! * – LSerni

1

sizeof(size) возвращает размер типа данных переменной size, которая, как правило, быть 4 для int - так что ваш буфер всегда содержит 4 байта. Вместо этого используйте malloc(size). Кроме того, вы только читаете и записываете один буфер - вам нужно использовать цикл для повторения процесса, если размер файла больше размера буфера.

Кроме того, используйте || вместо | для логического ИЛИ в if (fd_to < 0 | fd_from < 0).

+0

собирался сообщить об этих двух проблемах :) –

+0

Спасибо за ответ. Я внес эти изменения в свой код, но он все еще работает неправильно. Некоторые файлы по-прежнему не копируются, а некоторые все еще создаются с 0 байтами. – Ryan

+0

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

0

Это не то, почему она не работает (при условии, что это правильно), но -

Не пытайтесь сразу прочитать весь файл в память. Выделите фиксированный размер (1000 байтов) буфер и цикл, читающий кусок и записывающий фрагмент до конца файла.

+0

Почему лучше многократно читать и писать из буфера фиксированного размера вместо все это сразу? Особенно учитывая, что раньше я знал бы точный размер файла. – Ryan

+0

@ Ryan: Возможно, из-за проблем, связанных с памятью. –

+0

Как правило, неплохо не выделять что-либо, если вам действительно не нужно что-то выделять. Буферы с фиксированным размером прекрасны, если проблемы с размерами управляются по-другому (т. Е. По каналам). – Leushenko

-1

Я написал рабочий пример для вас

#include <stdio.h> 
#include <stdlib.h> 

int my_copy(char* file, char* new_file) 
{ 
    size_t n, m; 
    unsigned char buff[8192]; 

    /* File pointers */ 
    FILE *fp1, *fp2; 

    /* Open first file */ 
    fp1 = fopen(file, "r"); 
    if(fp1 == NULL) 
     return 1; 

    /* Open second file */ 
    fp2 = fopen(new_file, "w"); 
    if(fp2 == NULL) 
    { 
     fclose(fp1); 
     return 1; 
    } 

    /* Copy context */ 
    do { 
     n = fread(buff, 1, sizeof buff, fp1); 
     if (n) m = fwrite(buff, 1, n, fp2); 
     else m = 0; 
    } while ((n > 0) && (n == m)); 
    if (m) 
    { 
     /* Close files */ 
     fclose(fp1); 
     fclose(fp2); 
     return 1; 
    } 

    /* Close files */ 
    fclose(fp1); 
    fclose(fp2); 

    return 0; 
} 

int main() 
{ 
    /* Used variables */ 
    char file[20], new_file[20]; 
    int ret; 

    /* First file name */ 
    strcpy(file, "file.jpg"); 

    /* Second file name */ 
    strcpy(new_file, "newfile.jpg"); 

    /* Copy file to new file */ 
    ret = my_copy(file, new_file); 

    /* Check status */ 
    if (!ret) printf("Success\n"); 
    else printf("Error\n"); 

} 

Есть комментарии для каждой строки, надеюсь, что это будет полезно для вас.

+0

Будет ли это работать и с двоичным файлом? – Ryan

+0

Просто протестировал его, не работает. – Ryan

+0

Чтение и запись байтов байтом дорого. Но, возможно, это не проблема для вас. – Alfe

1

Предполагаете, что вам нужно скопировать файл произвольной длины, а аргумент , переданный функции, - это размер буфера, а не размер файла. Вам нужно сделать malloc(size), а не malloc(sizeof(size)), кстати. Самое главное, что вам нужно цикл, содержащий чтения() и записи(), что-то вроде

size_t rd_len, wr_len; 
do { 
    rd_len = read(fd_from, buffer, size); 
    wr_len = write(fd_to, buffer, size); 
    /* check that wr_len == rd_len */ 
    written += wr_len; 
while (wr_len > 0); 
Смежные вопросы