好问题
Good  Question
  • 首 页
  • 问题
    • PHP
    • JAVA
    • CPlusPlus
    • C#
    • SQL
  • 关 于
  • 联 系
用一个健壮的、安全、有效的方法复制文件 关闭 返回上一级  

用一个健壮的、安全、有效的方法复制文件
+ 查看更多

发布日期:2018-02-26 15:43
分类:CPlusPlus
浏览次数:188
我在寻找一种复制文件(二进制或文本形式)的好方法。我已经写了几个例子,每一个都有效。但是我想听一听老练的程序员的意见。
我缺少好的例子并且在寻找一种使用C++的有效方法。
ANSI-C方式
#include 
#include     // fopen, fclose, fread, fwrite, BUFSIZ
#include 
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(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}
POSIX方式 (K&R 在《C程序设计语言》中用了这种方式,它更加底层)
#include 
#include    // open
#include   // read, write, close
#include     // BUFSIZ
#include 
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(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}
KISS-C++-Streambuffer方式
#include 
#include 
#include 
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(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}
COPY-ALGORITHM-C++方式
#include 
#include 
#include 
#include 
#include 
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 begin_source(source);
    istreambuf_iterator end_source;
    ostreambuf_iterator 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(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}
OWN-BUFFER-C++方式
#include 
#include 
#include 
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(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}
LINUX方式 //需要内核版本 >= 2.6.33
#include 
#include   // sendfile
#include          // open
#include         // close
#include       // fstat
#include      // fstat
#include 
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(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}
环境
    GNU/LINUX (Archlinux)
    Kernel 3.3
    GLIBC-2.15, LIBSTDC++ 4.7 (GCC-LIBS), GCC 4.7, Coreutils 8.16
    Using RUNLEVEL 3 (Multiuser, Network, Terminal, no GUI)
    INTEL SSD-Postville 80 GB, filled up to 50%
    Copy a 270 MB OGG-VIDEO-FILE
复制步骤

 1. $ rm from.ogg
 2. $ reboot                           # 内核和文件系统内存正常
 3. $ (time ./program) &>> report.txt  # 执行程序,重定向程序输出并添加至文件
 4. $ sha256sum *.ogv                  # 校验总和
 5. $ rm to.ogg                        # 删除副本,但是不同步,使用了内核和文件系统缓冲区
 6. $ (time ./program) &>> report.txt  # 执行程序,重定向程序输出并添加至文件结果 (使用CPU时间)
程序 描述 无缓冲|有缓冲
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)
    我真的喜欢这个函数,因为它短而简单。据我所知,操作符<<被rdbuf()重载并且不变换任何东西。对吗?
谢谢
修正 1
我修改了所有例子的源代码,文件描述符的打开和关闭被包含在了clock()的测量范围内。源代码中没有其他的重要修改。结果没有改变!我也使用了时间来复核我的结果。
修正 2
ANSI C 版的例子改变了: while循环的条件不再调用feof(), 作为代替,我将fread()放入了条件。这个代码现在似乎执行快了10,000多个时钟周期。
测量方式改变了:之前的结果总是有缓冲的,因为我数次对每一个程序重复了旧的命令行rm to.ogv && sync && time ./program。现在
我为每一个代码重启系统。无缓冲的结果是新的但没有惊喜。无缓冲的结果确实没有变化。
如果我不删除旧副本,代码有不同的反应。用POSIX和SENDFILE重写一个已存在的缓冲文件是更快的。其他代码更慢。也许函数中的参数选择是truncate还是create会对此行为有影响。但是用相同的副本重写一个已存在的文件算不上一个用例。
用cp命令执行复制,无缓冲的话花了0.44秒,而有缓冲花了0.30秒。所以cp比POSIX版的例子慢一些。对我而言看起来不错。
也许我也应该增加使用boost::filesystem中的mmap()和copy_file()的例子和结果。
修正 3
我也把这个问题放在了一个博客页面上并将它扩展了一些。包含了splice(),它是Linux内核中的一个底层函数。也许更多使用Java的例子将接踵而至。 http://www.ttyhoney.com/blog/?page_id=69

回答

用健壮的方法复制文件:
int main()
{
    std::ifstream  src("from.ogv", std::ios::binary);
    std::ofstream  dst("to.ogv",   std::ios::binary);

    dst << src.rdbuf();
}
这是如此简单而易读,额外的开销是值得的。如果我们想依靠操作系统调用文件系统来做的更好。我确定boost库在它的文件系统类中有一个复制文件的方法。 有一个与文件系统交互的C语言方法:
#include 

int
copyfile(const char *from, const char *to, copyfile_state_t state, copyfile_flags_t flags);
上一篇在C++中如何把vector转换成array
在C++如何检查一个字符串包含另一个字符串下一篇
下一篇在C++如何检查一个字符串包含另一个字符串

最新文章

  • 函数`__construct`用来干嘛的
    发布日期:2018-03-26
  • 通过访客的IP得到他们的地区
    发布日期:2018-03-26
  • 合并两个PHP对象的最好的方法是什么?
    发布日期:2018-03-26
  • 该如何把一该如何把一个对象转化成数组?
    发布日期:2018-03-26
  • 什么是输出缓冲区?
    发布日期:2018-03-26
  • 在PHP中怎么把用逗号分隔的字符串分隔在一个数组里?
    发布日期:2018-03-26
  • 在PHP中使用foreach循环时查找数组的最后一个元素
    发布日期:2018-03-26
关于好问
收集整理一些有用的问题和回答,造福中国的程序旺和IT喵们!
友情链接
起飞页 
相关信息
版权声明
Copyright © 2016 - 2022  苏州卡达网络科技有限公司 备案号:苏ICP备09008221号