有没有办法直接将文件从一个文件夹复制到另一个文件夹而无需打开

时间:2015-11-04 16:04:21

标签: c

我知道这种复制文件的方式,我认为这是用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中运行的健壮程序。

4 个答案:

答案 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)

需要注意的一件事是,您要以最慢的方式复制,因为您要逐个字符地进行复制。一个改进是使用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;
}

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

这至少使库实现有机会检测到所有读写都是单个内存页。