我正在寻找一种复制文件(二进制或文本)的好方法。我写了几个样本,每个人都在工作。但是我想听听经验丰富的程序员的意见。

我错过了很好的例子,并寻找一种与C++兼容的方式。

ANSI-C-WAY

#include <iostream>
#include <cstdio>    // fopen, fclose, fread, fwrite, BUFSIZ
#include <ctime>
using namespace std;

int main() {
    clock_t start, end;
    start = clock();

    // BUFSIZE default is 8192 bytes
    // BUFSIZE of 1 means one chareter at time
    // good values should fit to blocksize, like 1024 or 4096
    // higher values reduce number of system calls
    // size_t BUFFER_SIZE = 4096;

    char buf[BUFSIZ];
    size_t size;

    FILE* source = fopen("from.ogv", "rb");
    FILE* dest = fopen("to.ogv", "wb");

    // clean and more secure
    // feof(FILE* stream) returns non-zero if the end of file indicator for stream is set

    while (size = fread(buf, 1, BUFSIZ, source)) {
        fwrite(buf, 1, size, dest);
    }

    fclose(source);
    fclose(dest);

    end = clock();

    cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
    cout << "CPU-TIME START " << start << "\n";
    cout << "CPU-TIME END " << end << "\n";
    cout << "CPU-TIME END - START " << end - start << "\n";
    cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}

POSIX-WAY (K&R在“C编程语言”中使用它,更底层)
#include <iostream>
#include <fcntl.h>   // open
#include <unistd.h>  // read, write, close
#include <cstdio>    // BUFSIZ
#include <ctime>
using namespace std;

int main() {
    clock_t start, end;
    start = clock();

    // BUFSIZE defaults to 8192
    // BUFSIZE of 1 means one chareter at time
    // good values should fit to blocksize, like 1024 or 4096
    // higher values reduce number of system calls
    // size_t BUFFER_SIZE = 4096;

    char buf[BUFSIZ];
    size_t size;

    int source = open("from.ogv", O_RDONLY, 0);
    int dest = open("to.ogv", O_WRONLY | O_CREAT /*| O_TRUNC/**/, 0644);

    while ((size = read(source, buf, BUFSIZ)) > 0) {
        write(dest, buf, size);
    }

    close(source);
    close(dest);

    end = clock();

    cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
    cout << "CPU-TIME START " << start << "\n";
    cout << "CPU-TIME END " << end << "\n";
    cout << "CPU-TIME END - START " << end - start << "\n";
    cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}

KISS-C++-Streambuffer-WAY
#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;

int main() {
    clock_t start, end;
    start = clock();

    ifstream source("from.ogv", ios::binary);
    ofstream dest("to.ogv", ios::binary);

    dest << source.rdbuf();

    source.close();
    dest.close();

    end = clock();

    cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
    cout << "CPU-TIME START " << start << "\n";
    cout << "CPU-TIME END " << end << "\n";
    cout << "CPU-TIME END - START " <<  end - start << "\n";
    cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}

COPY-ALGORITHM-C++-WAY
#include <iostream>
#include <fstream>
#include <ctime>
#include <algorithm>
#include <iterator>
using namespace std;

int main() {
    clock_t start, end;
    start = clock();

    ifstream source("from.ogv", ios::binary);
    ofstream dest("to.ogv", ios::binary);

    istreambuf_iterator<char> begin_source(source);
    istreambuf_iterator<char> end_source;
    ostreambuf_iterator<char> begin_dest(dest);
    copy(begin_source, end_source, begin_dest);

    source.close();
    dest.close();

    end = clock();

    cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
    cout << "CPU-TIME START " << start << "\n";
    cout << "CPU-TIME END " << end << "\n";
    cout << "CPU-TIME END - START " <<  end - start << "\n";
    cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}

OWN-BUFFER-C++-WAY
#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;

int main() {
    clock_t start, end;
    start = clock();

    ifstream source("from.ogv", ios::binary);
    ofstream dest("to.ogv", ios::binary);

    // file size
    source.seekg(0, ios::end);
    ifstream::pos_type size = source.tellg();
    source.seekg(0);
    // allocate memory for buffer
    char* buffer = new char[size];

    // copy file
    source.read(buffer, size);
    dest.write(buffer, size);

    // clean up
    delete[] buffer;
    source.close();
    dest.close();

    end = clock();

    cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
    cout << "CPU-TIME START " << start << "\n";
    cout << "CPU-TIME END " << end << "\n";
    cout << "CPU-TIME END - START " <<  end - start << "\n";
    cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}

LINUX-WAY //需要内核> = 2.6.33
#include <iostream>
#include <sys/sendfile.h>  // sendfile
#include <fcntl.h>         // open
#include <unistd.h>        // close
#include <sys/stat.h>      // fstat
#include <sys/types.h>     // fstat
#include <ctime>
using namespace std;

int main() {
    clock_t start, end;
    start = clock();

    int source = open("from.ogv", O_RDONLY, 0);
    int dest = open("to.ogv", O_WRONLY | O_CREAT /*| O_TRUNC/**/, 0644);

    // struct required, rationale: function stat() exists also
    struct stat stat_source;
    fstat(source, &stat_source);

    sendfile(dest, source, 0, stat_source.st_size);

    close(source);
    close(dest);

    end = clock();

    cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
    cout << "CPU-TIME START " << start << "\n";
    cout << "CPU-TIME END " << end << "\n";
    cout << "CPU-TIME END - START " <<  end - start << "\n";
    cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}

环境
  • GNU/LINUX(Archlinux)
  • 内核3.3
  • GLIBC-2.15,LIBSTDC++ 4.7(GCC-LIBS),GCC 4.7,Coreutils 8.16
  • 使用RUNLEVEL 3(多用户,网络,终端,无GUI)
  • INTEL SSD-Postville 80 GB,已填充高达50%
  • 复制270 MB的OGG-VIDEO-FILE

  • 重现的步骤
     1. $ rm from.ogg
     2. $ reboot                           # kernel and filesystem buffers are in regular
     3. $ (time ./program) &>> report.txt  # executes program, redirects output of program and append to file
     4. $ sha256sum *.ogv                  # checksum
     5. $ rm to.ogg                        # remove copy, but no sync, kernel and fileystem buffers are used
     6. $ (time ./program) &>> report.txt  # executes program, redirects output of program and append to file
    

    结果(已使用CPU时间)
    Program  Description                 UNBUFFERED|BUFFERED
    ANSI C   (fread/frwite)                 490,000|260,000
    POSIX    (K&R, read/write)              450,000|230,000
    FSTREAM  (KISS, Streambuffer)           500,000|270,000
    FSTREAM  (Algorithm, copy)              500,000|270,000
    FSTREAM  (OWN-BUFFER)                   500,000|340,000
    SENDFILE (native LINUX, sendfile)       410,000|200,000
    

    文件大小不变。
    sha256sum打印相同的结果。
    该视频文件仍然可以播放。

    问题
  • 您想要哪种方法?
  • 您知道更好的解决方案吗?
  • 您在我的代码中看到任何错误吗?
  • 您知道避免解决方案的原因吗?
  • FSTREAM(KISS,Streambuffer)
    我真的很喜欢这个,因为它确实很简短。据我所知,运算符<
    谢谢

    更新1
    我以这种方式更改了所有样本中的源,即在clock()的度量中包括文件描述符的打开和关闭。它们在源代码中没有其他重大变化。结果没有改变!我还花时间仔细检查了结果。

    更新2
    更改了ANSI C示例:while循环的条件不再调用feof(),而是将fread()移入了条件。看起来,该代码现在运行速度快了10,000个时钟。

    测量已更改:以前的结果总是被缓冲,因为我对每个程序重复了旧命令行rm to.ogv && sync && time ./program几次。现在,我为每个程序重新启动系统。无缓冲的结果是新的,并不令人惊讶。无缓冲的结果并没有真正改变。

    如果我不删除旧副本,程序的 react 会有所不同。使用POSIX和SENDFILE覆盖缓冲的现有文件更快,而其他所有程序则更慢。截断或创建的选项可能对此行为有影响。但是,用相同的副本覆盖现有文件不是现实的用例。

    使用cp执行复制需要0.44秒的未缓冲时间和0.30秒的缓冲时间。因此,cp比POSIX示例要慢一些。对我来说很好。

    也许我还从boost::filesystem中添加了mmap()和copy_file()的示例和结果。

    更新3
    我也将其放在博客页面上并对其进行了扩展。包括splice(),这是Linux内核中的底层函数。也许还会有更多的Java示例。
    http://www.ttyhoney.com/blog/?page_id=69

    最佳答案

    以理智的方式复制文件:

    #include <fstream>
    
    int main()
    {
        std::ifstream  src("from.ogv", std::ios::binary);
        std::ofstream  dst("to.ogv",   std::ios::binary);
    
        dst << src.rdbuf();
    }
    

    阅读起来如此简单直观,值得额外花费。如果我们做了很多事情,最好退回OS对文件系统的调用。我确定boost在其文件系统类中具有复制文件方法。

    有一种用于与文件系统进行交互的C方法:
    #include <copyfile.h>
    
    int
    copyfile(const char *from, const char *to, copyfile_state_t state, copyfile_flags_t flags);
    

    关于c++ - 以理智,安全,高效的方式复制文件,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10195343/

  • 10-11 17:51