c - 如何在C中复制文件?

标签 c file unix

我正在尝试用 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;
}

最佳答案

拥有与文件一样大的缓冲区对于大缓冲区值来说并不经济(“大”取决于平台和操作系统,但我怀疑它会超过,比如说,在达到 yield 递减之前超过 1 兆字节)。在某些系统上,您可以分配比物理可用内存更多的内存,缓冲区由磁盘上的交换区备份。在这一点上,如果你试图一口气复制整个文件,你可能会把大部分文件读和写到交换区,然后从交换区读回新文件,有效地加倍(在最少)复制次数。

所以我会使用一个循环。

您还需要检查内存分配和文件写入中的错误,并考虑将大小设为 int 可能会导致大文件出现问题(如今 2 GB 是可访问的文件大小,但它会溢出一个 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;
}

关于c - 如何在C中复制文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29422519/

相关文章:

c - 对 bool 值的读/写访问是否需要信号量

linux - 计算行数并按前缀词分组

sql - unix 中是否有类似 sql 中的减号运算符的实用程序

python - 为什么某些 Unix 命令在从 Python 内部调用时不起作用? (找不到命令)

c - Pthread 的问题;改变值(value)等

c - 汇编程序的大小是否与 C 程序几乎相同

c - 替代 C 中的许多 case switch 语句

c++ - 二进制文件如何工作? (从c++的角度来看)

c - 有什么理由在 "write-append"之后重新打开为 "read-only"吗?

javascript - 为什么选择文件时此表单不自动提交?