如何将输入流的前缀复制到 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 提出的解决方案,最终决定使用手动 read
和 write
循环。下面是我使用的代码(基于 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
循环,因为它使用流对象并且不仅仅用于复制文件。
在 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 提出的解决方案,最终决定使用手动 read
和 write
循环。下面是我使用的代码(基于 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
循环,因为它使用流对象并且不仅仅用于复制文件。