【问题标题】:How to copy a prefix of an input stream to a different stream in C++?如何将输入流的前缀复制到 C++ 中的不同流?
【发布时间】:2021-10-01 06:40:47
【问题描述】:

有一个巧妙的技巧可以用来在 C++ 中复制文件内容。如果我们有一个std::ifstream input; 用于一个文件,std::ofstream output; 用于第二个文件,则input 的内容可以像这样复制到output

output << input.rdbuf();

此解决方案复制整个第一个文件(或至少复制尚未使用的整个input 流)。我的问题是,如何仅将输入流的前缀(n 第一个字节)复制到输出流?

查看a bit of documentation后,我的想法是想办法缩短输入流,然后将其复制到输出流:

input.rdbuf()->pubsetbuf(input.rdbuf()->eback(), length_to_output);
output << input.rdbuf();

问题是,这不会编译为eback 不是public。这只是为了说明我的想法。我知道我可以将整个输入复制到一个字符串中,然后将它的一个子字符串复制到输出中,但我担心它会降低时间和内存效率。因此,我考虑使用流来代替,就像上面一样。

【问题讨论】:

  • “输入流的一部分”是什么意思?一些初始部分?一些尾随部分?中间的某个部分?您如何准确地定义复制哪个“输入流的一部分”?
  • @SamVarshavchik 我的意思是n 流的第一个字节。我会更新问题。

标签: c++ linux file file-io stream


【解决方案1】:

如果要复制输入流的第一个 length_to_output 字节,可以使用 std::istream_iteratorstd::ostream_iterator 以及 std::copy_n

std::copy_n(std::istream_iterator<char>(input),
            length_to_output,
            std::ostream_iterator<char>(output));

【讨论】:

  • 感谢这个想法,但恐怕这个解决方案太慢了。我针对使用rdbufstd::filesystem::copy_filesendfile 对它进行了基准测试(在复制整个文件的情况下)。它慢了大约 2 个数量级。复制 1GB 文件时,需要27.08 秒,而其他方法大约需要0.60 秒。
  • @janekb04 使用流缓冲区迭代器可能更有效(例如std::istreambuf_iterator。这种方法的好处是它只复制您需要的字节数,而不是整个文件(几乎总是可以非常有效地完成)。唯一的其他解决方案是使用普通的read
  • std::istreambuf_iterator 确实更快-大约需要3.28 秒-但这仍然比其他解决方案慢得多。我想我得试试read:stackoverflow.com/a/4063994/12501684
【解决方案2】:

我尝试了不同的解决方案,包括@Some程序员老兄提出的解决方案,最终决定使用手动readwrite循环。下面是我使用的代码(基于this,稍作修改),底部是基准测试结果:

bool stream_copy_n(std::istream& in, std::ostream& out, std::size_t count) noexcept
{
    const std::size_t buffer_size = 256 * 1024; // a bit larger buffer
    std::unique_ptr<char[]> buffer = std::make_unique<char[]>(buffer_size); // allocated on heap to avoid stack overflow
    while(count > buffer_size)
    {
        in.read(buffer.get(), buffer_size);
        out.write(buffer.get(), buffer_size);
        count -= buffer_size;
    }

    in.read(buffer.get(), count);
    out.write(buffer.get(), count);

    return in.good() && out.good(); // returns if copy was successful 
}

使用Unix内置time命令,real时间获得的基准测试结果(复制整个文件1GB文件时):

Method Time
Linux C function sendfile 0.59
std::filesystem::copy_file 0.60
Unix command cp 0.69
Manual read and write loop presented above 0.78
output &lt;&lt; input.rdbuf() 0.96
std::copy_n(std::istreambuf_iterator&lt;char&gt;(input), std::filesystem::file_size(inputFilePath), std::ostreambuf_iterator&lt;char&gt;(output)); 3.28
std::copy_n(std::istream_iterator&lt;char&gt;(input), std::filesystem::file_size(inputFilePath), std::ostream_iterator&lt;char&gt;(output)); 27.37

尽管它不是最快的,但我还是选择了read-write 循环,因为它使用流对象并且不只用于复制文件。

【讨论】:

  • 根据你想从文件中复制多少(以及用于源文件和目标文件的磁盘类型,如果确实是文件),你可以使用线程并行读取和写入不同的部分.您还可以在代码中试验不同的缓冲区大小。
  • @Someprogrammerdude 感谢您的建议。不幸的是,这是一个更大的并行系统的一部分,这个代码必须是单线程的。我尝试了不同的缓冲区大小,发现 256 KB 是最佳大小,如上所述。
猜你喜欢
  • 1970-01-01
  • 2011-05-09
  • 2012-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-04
  • 2015-10-27
  • 2015-05-14
相关资源
最近更新 更多