2015-11-04 32 views
0

我知道复制文件,我认为这是在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中是否有任何这样的命令或技巧,我可以使用。因为我写一个健壮的程序应该在Linux和Windows的工作,我不能用

system("cp sourcefile.txt destinationfile.txt"); 

命令。

+0

你真的想创建一个副本吗?或者你最终只是移动文件,也就是在复制后删除源文件? – alk

+0

@alk不,我想复制和粘贴,而不是剪切和粘贴。 – ITguy

+3

文件复制的任何实现都必须打开该文件并使用系统调用复制字节。 –

回答

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

这是怎么了我提出在过去文件,而无需打开它:

#include <stdio.h> 
int main() 
{ 
    rename("C:\\oldFile.txt", "C:\\newfile.txt"); 
    return 0; 
} 
+0

投票给你,但这会删除我的原始文件,我需要保持它们不变。 – ITguy

+1

如果源和目标不在同一个文件系统中,'rename()'可能会失败。 – alk

+1

移动与复制不同... –

1

有一点要注意是,你复制的最慢的方式,因为你按字符进行。一个改进是完全复制线或更大的文本块,使用fgetsfputs

更妙的是不将文件复制为文本文件,而只是作为一个二进制块。这是通过用b标志以二进制模式打开文件来实现的,例如, target = fopen(target_file, "wb");并使用freadfwrite代替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; 
} 
+0

为什么要使用内存映射文件?只需以二进制模式打开并读取大块,然后将块写入目标。如果你希望它尽可能快,那么对读/写进行线程化处理,这样你就不会序列化延迟(尽管这可能会导致序列化延迟进一步向上延伸) –

+0

AFAIK大多数操作系统都进行了一些优化当使用mmf时,但我恐怕没有确实的事实。 – Superlokkus

+0

性能应该类似于复制与块大小对齐的块:操作系统可以优化它是否可以仅用无偏移量分页整个块。 – Davislor

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; 
} 

这至少给了库实现的机会,以检测所有读取和写入都是单个内存页面。

相关问题