我知道这种复制文件的方式,我认为这是用C语言复制文件的标准方法。
#include <stdio.h>
#include <stdlib.h>
int main()
{
char ch, source_file[20], target_file[20];
FILE *source, *target;
printf("Enter name of file to copy\n");
gets(source_file);
source = fopen(source_file, "r");
if( source == NULL )
{
printf("Press any key to exit...\n");
exit(EXIT_FAILURE);
}
printf("Enter name of target file\n");
gets(target_file);
target = fopen(target_file, "w");
if( target == NULL )
{
fclose(source);
printf("Press any key to exit...\n");
exit(EXIT_FAILURE);
}
while( ( ch = fgetc(source) ) != EOF )
fputc(ch, target);
printf("File copied successfully.\n");
fclose(source);
fclose(target);
return 0;
但这种方式会打开文件并逐行复制。我要复制的文件很多,很多。这种方式将非常非常长。有没有办法实现我直接复制这些文件的目标。我知道终端或命令提示符与C语言完全不同,但是简单
cp sourcefile.txt destinationfile.txt
可以做到这一点。
我可以使用C中的任何此类命令或技巧。我不能用
system("cp sourcefile.txt destinationfile.txt");
命令,因为我正在编写一个应该在Linux和Windows中运行的健壮程序。
答案 0 :(得分:3)
那么,你认为cp
命令本身对复制文件有什么作用?如果在读取模式下打开源文件,目标文件是写入模式并通过二进制块复制所有内容!如果你将其他选项传递给cp
,可能会涉及更多的事情,但副本本身并不比那更神奇。
话虽如此,你做的不是那个。您正在逐个字符地复制文件。即使标准库执行了一些缓冲,也可以在可以避免的情况下重复调用函数。并且...... 永远不会使用获取。它已被弃用多年,因为它不安全。如果用户输入looong文件名(超过19个字符),则会出现缓冲区溢出。并且不要忘记测试所有 io函数,包括输出函数。在外部媒体(例如USB密钥)上写入大文件时,您可能会在设备上占用空间,而您编程只会说它可以成功复制。
复制循环可能类似于:
#define SIZE 16384
char buffer[SIZE];
int crin, crout = 0;
while ((crin = fread(buffer, 1, SIZE, source)) > 0) {
crout = fwrite(buffer, 1, crin, target);
if (crout != crin) { /* control everything could be written */
perror("Write error");
crout = -1;
break;
}
if (crin < 0) { /* test read error (removal of amovible media, ...) */
perror("Read error");
}
这里的低级优化是直接使用posix函数而不是标准库函数,因为只要你在大块中使用二进制IO,标准库的缓冲就没有优势,你只需要它的开销
答案 1 :(得分:1)
这就是我过去移动文件而不必打开文件的方式:
#include <stdio.h>
int main()
{
rename("C:\\oldFile.txt", "C:\\newfile.txt");
return 0;
}
答案 2 :(得分:1)
需要注意的一件事是,您要以最慢的方式复制,因为您要逐个字符地进行复制。一个改进是使用fgets
和fputs
更好的方法是不将文件复制为文本文件,而是将其作为二进制块。这是通过使用b
标志以二进制模式打开文件来实现的,例如, target = fopen(target_file, "wb");
并使用fread
和fwrite
代替put字符函数。
在这两种情况下,您都必须使用具有合理大小的临时缓冲区(可以是文件的大小或固定的)。确定最佳尺寸并非易事。
另一种复制方式,根据我的操作系统教授 cp
做什么,是使用memory mapped files。
遗憾的是,如何使用内存映射文件是不可移植的,但取决于您的操作系统,即平台。对于unix,mmap
的联机帮助页是您的朋友。这是我的unix实现示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <errno.h>
#include <time.h>
#include <string.h>
#include <sys/shm.h>
#include <signal.h>
#include <stdbool.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, const char * argv[]) {
if (argc != 3)
{
fprintf(stderr, "Usage %s <SourceFile> <DestinationFile>\n",argv[0]);
return EXIT_FAILURE;
}
int source_file_desc = open(argv[1], O_RDONLY);
if (source_file_desc == -1) {
perror("Can't open source file");
return EXIT_FAILURE;
}
struct stat source_info;
if (stat(argv[1], &source_info) != 0) {
perror("Can't get source file infos");
return EXIT_FAILURE;
}
void *source_mem = mmap(NULL, source_info.st_size, PROT_READ, MAP_FILE|MAP_PRIVATE, source_file_desc, 0);
if (source_mem == MAP_FAILED) {
perror("Mapping source file failed");
return EXIT_FAILURE;
}
int destination_file_desc = open(argv[2], O_TRUNC|O_CREAT|O_RDWR);
if (destination_file_desc == -1) {
perror("Can't open destination file");
}
if (chmod(argv[2], source_info.st_mode) != 0) {
perror("Can't copy file permissions");
}
if (lseek(destination_file_desc, source_info.st_size-1, SEEK_SET) == -1) {
perror("Can'T seek to new end of destination file");
}
unsigned char dummy = 0;
if (write(destination_file_desc, &dummy, 1) == -1)
{
perror("Couldn't write dummy byte");
}
void *destination_mem = mmap(NULL, source_info.st_size, PROT_WRITE,MAP_FILE|MAP_SHARED, destination_file_desc,0);
if (destination_mem == MAP_FAILED) {
perror("Mapping destination file failed");
}
memcpy(destination_mem, source_mem, source_info.st_size);
munmap(source_mem,source_info.st_size);
munmap(destination_mem, source_info.st_size);
close(source_file_desc);
close(destination_file_desc);
return EXIT_SUCCESS;
}
答案 3 :(得分:0)
如果对一个副本的任何更改都会影响另一个副本不是问题,则可以创建指向该文件的链接。这是如何工作的取决于操作系统。
如果您想仅使用标准库尽可能优化文件副本,我建议(未经测试):
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern bool copy_file( FILE* dest, FILE* restrict src );
static bool error_helper( const char* file, int line, const char* msg );
#if defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) || defined(__i386) || defined(_M_IX86) || defined(_X86_) || defined(__X86__) || defined(__I86__) || defined(__INTEL__) || defined(__386)
# define PAGE_SIZE 4096U
#else
# error "Define the page size on your system, or use a system call such as sysconf() to find it."
#endif
#define non_fatal_stdlib_error() error_helper( __FILE__, __LINE__, strerror(errno) )
bool copy_file( FILE* dest, FILE* restrict src )
{
errno = 0;
if ( !(dest = freopen( NULL, "w+", dest )) )
return non_fatal_stdlib_error();
/* Try to help the library out by turning buffering off and allocating an aligned block; it might be able to detect that at runtime.
* On the other hand, the unbuffered implementation might be worse. */
setvbuf( src, NULL, _IONBF, BUFSIZ );
setvbuf( dest, NULL, _IONBF, BUFSIZ );
char* const buffer = aligned_alloc( PAGE_SIZE, PAGE_SIZE );
if (!buffer)
return non_fatal_stdlib_error();
size_t n = fread( buffer, 1, PAGE_SIZE, src );
while ( PAGE_SIZE == n ) {
const size_t written = fwrite( buffer, 1, PAGE_SIZE, dest );
if ( written != PAGE_SIZE )
return non_fatal_stdlib_error();
n = fread( buffer, 1, PAGE_SIZE, src );
} // end while
if (ferror(src))
return non_fatal_stdlib_error();
if ( n > 0 ) {
const size_t written = fwrite( buffer, 1, n, dest );
if ( written != n )
return non_fatal_stdlib_error();
}
return true;
}
bool error_helper( const char* file, int line, const char* msg )
{
fflush(stdout);
fprintf( stderr, "Error at %s, line %d: %s.\n", file, line, msg );
fflush(stderr);
return false;
}
这至少使库实现有机会检测到所有读写都是单个内存页。