C++ Copy a Stream Object

How do I copy the contents of one stream to another?

From .NET 4.5 on, there is the Stream.CopyToAsync method

input.CopyToAsync(output);

This will return a Task that can be continued on when completed, like so:

await input.CopyToAsync(output)

// Code from here on will be run in a continuation.

Note that depending on where the call to CopyToAsync is made, the code that follows may or may not continue on the same thread that called it.

The SynchronizationContext that was captured when calling await will determine what thread the continuation will be executed on.

Additionally, this call (and this is an implementation detail subject to change) still sequences reads and writes (it just doesn't waste a threads blocking on I/O completion).

From .NET 4.0 on, there's is the Stream.CopyTo method

input.CopyTo(output);

For .NET 3.5 and before

There isn't anything baked into the framework to assist with this; you have to copy the content manually, like so:

public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[32768];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write (buffer, 0, read);
}
}

Note 1: This method will allow you to report on progress (x bytes read so far ...)

Note 2: Why use a fixed buffer size and not input.Length? Because that Length may not be available! From the docs:

If a class derived from Stream does not support seeking, calls to Length, SetLength, Position, and Seek throw a NotSupportedException.

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

I tried different solutions, including the one presented by @Some programmer dude, and ultimately decided to go with a manual read and write loop. Below is the code that I used (based on this, with small modifications) and at the bottom are the benchmark results:

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
}

The benchmark results (when copying an entire file 1GB file) acquired using the built in Unix time command, real time:







































MethodTime
Linux C function sendfile0.59
std::filesystem::copy_file0.60
Unix command cp0.69
Manual read and write loop presented above0.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

how copy from one stringstream object to another in C++?

Indeed, streams are non-copyable (though they are movable).

Depending on your usage, the following works quite well:

#include <iostream>
#include <sstream>

int main()
{
std::stringstream ss1;
ss1 << "some " << 123 << " stuff" << std::flush;

std::stringstream ss2;
ss2 << ss1.rdbuf(); // copy everything inside ss1's buffer to ss2's buffer

std::cout << ss1.str() << std::endl;
std::cout << ss2.str() << std::endl;
}

Output:

some 123 stuff

some 123 stuff

Copying streams in C++

The construction of std::ostream requires a buffer (not NULL).

In addition, basic_stream objects are not copyable;

basic_ostream( const basic_ostream& rhs ) = delete;

Reference;

http://en.cppreference.com/w/cpp/io/basic_ostream/basic_ostream

Try something more like this;

// ...
std::stringbuf buffer;
std::ostream tempStream(&buffer);
// ...

To associate a buffer with the stream.

Following on some of the discussions and edits;

In general, I would not directly manipulate the buffer, you should rather use the stream instead tempStream.write(...). The exact details are beyond the immediate question/problem; being the initialisation of the first stream with the buffer and streaming content into that stream. If all you want to do with the code is check if the data is in the buffer, then you could use tempStream.rdbuf()->sgetn(...).

You have already mentioned that this is part of a larger problem.

In the context of some the comments here and the original problem; this could be a case in which direct manipulation of the buffer is needed (in much the same way as the stream would). Your implementation would need to be well tested since this is not "the usual" way of working with streams, but it can work; .rdbuf() is the manner in which you can get to the underlying buffer. I don't have an exact snippet (maybe that's another question), but you can "clear the buffer" by resetting the position(s) of the put and get areas to be the same (see the positioning, put and get area functions of the buffer - std::ostream::seekp was mentioned as being used to deal with this). I think you standard library implementation of stringstream could also offer some useful hints.

How can I clone MemoryStream object?

I have solved my problem! :)

The things which were missing are marked with //*.
Here is the code as it should be:

var xmlReaderSettings = new XmlReaderSettings();

stream.Position = 0;//*
xmlReaderSettings.IgnoreWhitespace = true;
using (var newMemoryStream = new MemoryStream())
{
stream.CopyTo(newMemoryStream);
newMemoryStream.Position = 0; //*
using (var newReader = XmlReader.Create(newMemoryStream, xmlReaderSettings))
{
newReader.MoveToContent(); //*

Doing some stuff...
}
}

cannot assign or copy iostream object?

Your code attempts to turn one istream, the one passed to the constructor, into two istreams, the one that was passed to the constructor and ais. An istream object represents that actual stream itself. There is only one stream and there is no way to somehow turn it into two streams.

It's not even clear what that would mean. If there's some data on the stream, does whichever stream reads first get it? Or do they both get it? If so, who or what duplicates it?

An istream is like a file itself. You cannot turn one file into two files without copying the data from one to the other yourself. You can, however, have as many references or pointers to the same istream as you want. The solution to your problem is probably to make ais a reference.

Why are iostreams not copyable?

Copying and moving are value-semantic operations. To define them, you first have to decide what properties of a class give its objects distinct values. This point was at first largely sidestepped for the iostreams library, and then C++11 took a different direction incompatible with such a copy constructor.

The state of a stream object comprises two parts: A pointer to a stream buffer with its associated state, and formatting information. Since C++98, rdbuf, rdstate, and copyfmt expose this information separately.

Since C++11, stream classes also have a protected interface including a move constructor (and a member called move) which copies the format but not the stream buffer pointer. This commits iostream to treating the formatting information exclusively as the state of the stream object.

If streams were made copyable at this point, it would only do copyfmt and not the rest.

The choice to exclude rdbuf from the value state may be due to the further-muddled value semantics of derived classes such as std::fstream, which not only expose access to a stream buffer, but also embed and own it.

std::ifstream f( path + filename ); // Owns, or even "is," a file.
std::istream i = f; // Observes an externally-managed file.

std::istream i2 = i; // OK, copy a shallow reference.
std::ifstream f2 = f; // Error, ifstream is more than a shallow reference.

std::istream i3 = std::move( f ); // Error? Would retain a reference to an rvalue.
std::ifstream f3 = std::move( f ); // OK: full copy including the file buffer.

The semantics could be consistent in some fashion, but it would be a lot of confusion for a moderate gain.



Related Topics



Leave a reply



Submit