如何将输入流的前缀复制到 C++ 中的不同流?

How to copy a prefix of an input stream to a different stream in C++?

在 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。这只是为了说明我的想法。我知道我可以将整个输入复制到一个字符串中,然后将它的一个子字符串复制到输出中,但我担心它会降低时间和内存效率。因此,我考虑过像上面那样使用流。

如果要复制输入流的前 length_to_output 个字节,可以使用 std::istream_iterator and an std::ostream_iterator together with std::copy_n:

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

我尝试了不同的解决方案,包括@Some programmer dude 提出的解决方案,最终决定使用手动 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 命令获取的基准测试结果(复制整个文件 1GB 文件时),real 时间:

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 << input.rdbuf() 0.96
std::copy_n(std::istreambuf_iterator<char>(input), std::filesystem::file_size(inputFilePath), std::ostreambuf_iterator<char>(output)); 3.28
std::copy_n(std::istream_iterator<char>(input), std::filesystem::file_size(inputFilePath), std::ostream_iterator<char>(output)); 27.37

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