C 读取其余的数据,或者只写一个大小为n的元素——让fwrite完成所有的工作。(此项有问题,是否应该写入所有数据,但在我的版本中,在出现错误之前不可能进行短写。) 您还应该测试读取错误:只需在循环结束时测试ferror(f1)
注意,您可能需要禁用输入和输出文件上的缓冲,以防止三重缓冲:首先读取f1缓冲区,其次在代码中,第三次写入f2缓冲区:C 读取其余的数据,或者只写一个大小为n的元素——让fwrite完成所有的工作。(此项有问题,是否应该写入所有数据,但在我的版本中,在出现错误之前不可能进行短写。) 您还应该测试读取错误:只需在循环结束时测试ferror(f1),c,file-io,copy,stdio,C,File Io,Copy,Stdio,注意,您可能需要禁用输入和输出文件上的缓冲,以防止三重缓冲:首先读取f1缓冲区,其次在代码中,第三次写入f2缓冲区: setvbuf(f1,NULL,_-IONBF,0); setvbuf(f2,NULL,_-IONBF,0); (内部缓冲区的大小应该是BUFSIZ。)遗憾的是,它使用了fgetc,这是非常低效的。这一点很好!虽然它非常清晰和可移植,但它确实缺乏性能。@David:fgetc()效率低吗?Stdio将使用BUFSIZ大小的缓冲区(在我的系统上是8192字节)进行自己的缓冲。如果
setvbuf(f1,NULL,_-IONBF,0);
setvbuf(f2,NULL,_-IONBF,0);
(内部缓冲区的大小应该是BUFSIZ。)遗憾的是,它使用了fgetc,这是非常低效的。这一点很好!虽然它非常清晰和可移植,但它确实缺乏性能。@David:fgetc()效率低吗?Stdio将使用BUFSIZ大小的缓冲区(在我的系统上是8192字节)进行自己的缓冲。如果您使用的是MSVC++,则在单线程程序中定义、CRT、DISABLE、PERFCRIT锁定。getc()可能比fgetc()稍快一些,因为它是作为宏实现的,但是我怀疑CPU分支是否会成为瓶颈——从磁盘读取会是这样。@j\U random:根据我测试过它的经验,8k缓冲区从未达到最佳I/O性能。有时16k缓冲区的速度实际上是原来的两倍。显然,MS不希望每个文件句柄都有巨大的内存开销,所以他们已经妥协了,但在复制文件时,您可能需要不同的妥协。此外,如果您对每个字符执行两个filehandle操作,那么簿记开销可能非常大(检查边界、更新文件指针位置等)。唯一知道的方法是编写一些代码,然后查看ofc。您的示例无法按已写入的量抵消buf,这将导致不完整的写入从顶部重新启动。总有一个bug。OP要求一个可移植的解决方案,但我发现它在Windows上不起作用。首先,缺少
poll()
,而ssize\u t
是POSIX扩展。不是不可克服的,但代码肯定不能按原样工作。我喜欢它,简单、干净、有效。我使用4096作为我的BUFSIZ,但我假设512的任何倍数都应该表现良好。@jonathan I这段代码如果BUFSIZ的大小是多少?我的源文件可能约为50 MB。?那么,哪种大小适合我呢?BUFSIZ是在
中定义的,是平台上文件缓冲区的合适大小。如果要控制缓冲区大小,请使用其他名称并指定其大小:enum{buffer_size=4096}代码>或任何您想要使用的内容。在广泛的范围内,较大的缓冲区大小会更快,但是从4kib增加到256kib通常并没有那么大,您必须权衡用于缓冲区的空间。根据您的平台(例如,服务器与移动设备),您可以调整您的选择。4kib到64kib对于大多数用途都是足够的。我喜欢简单。为什么这样的功能没有直接包含在glibc中?显然,这种方法比中建议的解决方案更简单和(更)可移植。。。有什么缺点吗?
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;
}
/*
@(#)File: $RCSfile: fcopy.c,v $
@(#)Version: $Revision: 1.11 $
@(#)Last changed: $Date: 2008/02/11 07:28:06 $
@(#)Purpose: Copy the rest of file1 to file2
@(#)Author: J Leffler
@(#)Modified: 1991,1997,2000,2003,2005,2008
*/
/*TABSTOP=4*/
#include "jlss.h"
#include "stderr.h"
#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
const char jlss_id_fcopy_c[] = "@(#)$Id: fcopy.c,v 1.11 2008/02/11 07:28:06 jleffler Exp $";
#endif /* lint */
void fcopy(FILE *f1, FILE *f2)
{
char buffer[BUFSIZ];
size_t n;
while ((n = fread(buffer, sizeof(char), sizeof(buffer), f1)) > 0)
{
if (fwrite(buffer, sizeof(char), n, f2) != n)
err_syserr("write failed\n");
}
}
#ifdef TEST
int main(int argc, char **argv)
{
FILE *fp1;
FILE *fp2;
err_setarg0(argv[0]);
if (argc != 3)
err_usage("from to");
if ((fp1 = fopen(argv[1], "rb")) == 0)
err_syserr("cannot open file %s for reading\n", argv[1]);
if ((fp2 = fopen(argv[2], "wb")) == 0)
err_syserr("cannot open file %s for writing\n", argv[2]);
fcopy(fp1, fp2);
return(0);
}
#endif /* TEST */
#include "stderr.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
void err_syserr(const char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)\n", errnum, strerror(errnum));
exit(1);
}