在C中试用和真正的简单文件复制代码?

这看起来像一个简单的问题,但我没有发现任何类似的东西.

由于C中没有文件复制功能,我们必须自己实现文件复制,但是我不喜欢重新发明轮子,即使是这样的微不足道的东西,所以我想问云:

>使用fopen()/ fread()/ fwrite()),您建议使用什么代码进行文件复制?

>使用open()/ read()/ write(),您建议使用什么代码进行文件复制?

这个代码应该是可移植的(windows / mac / linux / bsd / qnx / younameit),稳定的,时间测试的,快速的,内存高效的等等.进入特定系统的内部来挤压一些更多的性能是受欢迎的(如获取文件系统集群大小) .

这似乎是一个琐碎的问题,但是例如,CP命令的源代码不是10行C代码.

解决方法

就实际的I / O而言,我将数据从一个流复制到另一个流程的代码写在百万次以上,就像这样.在成功时返回0,或者在错误时设置了errno(在这种情况下可能会复制任何数量的字节).

请注意,对于复制常规文件,您可以跳过EAGAIN的东西,因为常规文件总是阻止I / O.但是不可避免地,如果你写这个代码,有人会使用它在其他类型的文件描述符,所以认为它是一个免费的.

一个文件特定的GNU cp优化,我没有在这里烦恼,对于0个字节的长块而不是写你只是通过寻求结束扩展输出文件.

void block(int fd,int event) {
    pollfd topoll;
    topoll.fd = fd;
    topoll.events = event;
    poll(&topoll,1,-1);
    // no need to check errors - if the stream is bust then the
    // next read/write will tell us
}

int copy_data_buffer(int fdin,int fdout,void *buf,size_t bufsize) {
    for(;;) {
       void *pos;
       // read data to buffer
       ssize_t bytestowrite = read(fdin,buf,bufsize);
       if (bytestowrite == 0) break; // end of input
       if (bytestowrite == -1) {
           if (errno == EINTR) continue; // signal handled
           if (errno == EAGAIN) {
               block(fdin,POLLIN);
               continue;
           }
           return -1; // error
       }

       // write data from buffer
       pos = buf;
       while (bytestowrite > 0) {
           ssize_t bytes_written = write(fdout,pos,bytestowrite);
           if (bytes_written == -1) {
               if (errno == EINTR) continue; // signal handled
               if (errno == EAGAIN) {
                   block(fdout,POLLOUT);
                   continue;
               }
               return -1; // error
           }
           bytestowrite -= bytes_written;
           pos += bytes_written;
       }
    }
    return 0; // success
}

// Default value. I think it will get close to maximum speed on most
// systems,short of using mmap etc. But porters / integrators
// might want to set it smaller,if the system is very memory
// constrained and they don't want this routine to starve
// concurrent ops of memory. And they might want to set it larger
// if I'm completely wrong and larger buffers improve performance.
// It's worth trying several MB at least once,although with huge
// allocations you have to watch for the linux 
// "crash on access instead of returning 0" behavIoUr for Failed malloc.
#ifndef FILEcopY_BUFFER_SIZE
    #define FILEcopY_BUFFER_SIZE (64*1024)
#endif

int copy_data(int fdin,int fdout) {
    // optional exercise for reader: take the file size as a parameter,// and don't use a buffer any bigger than that. This prevents 
    // memory-hogging if FILEcopY_BUFFER_SIZE is very large and the file
    // is small.
    for (size_t bufsize = FILEcopY_BUFFER_SIZE; bufsize >= 256; bufsize /= 2) {
        void *buffer = malloc(bufsize);
        if (buffer != NULL) {
            int result = copy_data_buffer(fdin,fdout,buffer,bufsize);
            free(buffer);
            return result;
        }
    }
    // Could use a stack buffer here instead of failing,if desired.
    // 128 bytes ought to fit on any stack worth having,but again
    // this Could be made configurable.
    return -1; // errno is ENOMEM
}

打开输入文件

int fdin = open(infile,O_RDONLY|O_BINARY,0);
if (fdin == -1) return -1;

打开输出文件是很巧妙的.作为基础,你想要:

int fdout = open(outfile,O_WRONLY|O_BINARY|O_CREAT|O_Trunc,0x1ff);
if (fdout == -1) {
    close(fdin);
    return -1;
}

但有混淆因素:

>你需要特殊情况下文件是一样的,我不记得如何做可移植的.
>如果输出文件名是目录,则可能需要将文件复制到目录中.
>如果输出文件已经存在(使用O_EXCL打开以确定此错误并检查EEXIST是否有错误),则可能需要执行与cp -i不同的操作.
>您可能希望输出文件的权限反映输入文件的权限.
>您可能需要复制其他平台特定的元数据.
>您可能希望或可能不希望取消输出文件错误.

显然,所有这些问题的答案可能是“做同样的cp”.在这种情况下,原始问题的答案是“忽略我或任何其他人所说的一切,并使用cp的来源”.

获取文件系统的集群大小的Btw是无用的.在您通过磁盘块大小后,您将几乎总是看到速度随着缓冲区大小而增加.

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...