0

I'm using a BinaryReader to parse certain data. I would like to copy the read bytes to a secondary stream. How can I do that?

If I derive from BinaryReader (I need to do that for a different reason anyway, actually), which methods do I need to overwrite for correct behavior?

mafu
  • 31,798
  • 42
  • 154
  • 247
  • 1
    Dear downvoter: Mind explaining what the problem is? Is this a duplicate (even tho I could not find this in search)? – mafu Mar 22 '15 at 21:04
  • 1
    Do not derive, encapsulate. Create a new stream class, and create the `BinaryReader` over this stream, while your own class will wrap the inner stream in itself. So when `BinaryReader` calls `Read`, it will be on your stream, which will do the reading from the underlying stream, and writing the data it read to another stream during the same `Read` operation. – Luaan Mar 23 '15 at 14:35

2 Answers2

2

BinaryReader isn't a real Stream... It is more an overlay on another stream. You could:

using (var yourStream = /* ... */)
using (var br = new BinaryReader(yourStream))
{
    using (var ms = new MemoryStream())
    {
        // Important the true, so that the ms remains open!
        using (var bw = new BinaryWriter(ms, Encoding.UTF8, true))
        {
            var readed = br.ReadInt32();
            bw.Write(readed); 
            // All the basic types are supported by various overlaods
        }

        // Here you have your ms... If you want to reread it:
        ms.Position = 0;

        using (var br2 = new BinaryReader(ms, Encoding.UTF8, true))
        {
            // Reread it
        }

        // Or copy it to another stream, or do whatever you want
    }
}

If you simply want to duplicate a stream on another stream while you are reading it, you can use something like:

public class CopyStream : Stream
{
    // This is the stream you want to read your data from
    public Stream ReadStream { get; set; }

    // This is the "logger" stream, where a copy of read data will be put
    public Stream LogStream { get; set; }

    public override int Read(byte[] buffer, int offset, int count)
    {
        int res = ReadStream.Read(buffer, offset, count);

        if (LogStream != null)
        {
            if (res > 0)
            {
                LogStream.Write(buffer, offset, res);
            }
        }

        return res;
    }

    public override int ReadByte()
    {
        int res = ReadStream.ReadByte();

        if (LogStream != null)
        {
            if (res >= 0)
            {
                LogStream.WriteByte((byte)res);
            }
        }

        return res;
    }

    public override bool CanSeek
    {
        get { return false; }
    }
}

You'll need to implement/throw NotImplementedException() many other methods, because Stream is an abstract class. Right click on the Stream, Implement Abstract Class will speedup this.

The basic use is:

var cs = new CopyStream
    {
        ReadStream = yourStream,
        LogStream = whereyouwanttoWriteStream,
    };

using (var br = new BinaryReader(CopyStream, Encoding.UTF8, true))
{
    // Here you can read normally from the BinaryReader.
    // Everything you read will be copied to LogStream 
}

Note that subclassing BinaryReader is quite complex, because each method loads the data in a different way, so there isn't a single "point of interception".

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • I'm afraid that won't work for me, because the source is not seekable (it is a network stream). – mafu Mar 22 '15 at 22:05
  • In the example the MemoryStream is where you copy the data. Where there is the bw.Write you first should have a br.Read from your stream – xanatos Mar 23 '15 at 00:05
  • Indeed. But I am using _many_ Reads, so I want to automate the process of cloning the data. – mafu Mar 23 '15 at 14:02
  • Right. That is what I was looking for. But the question is: Which methods do I need to override? I'm fairly certain I only need to override a few of them, as they are re-used internally. But which ones? – mafu Mar 23 '15 at 14:29
  • @mafu The two `Read` I wrote, and the `Dispose(bool disposing)` if you want your other streams to be automatically disposed. The `Stream.Close` calls the `Dispose(bool disposing)`, so the `Stream.Close` is already ok. For everything else... There shouldn't be anything to write... And then, if there is, you'll get a `NotImplementedException` and you'll find it :-) – xanatos Mar 23 '15 at 14:35
  • Ok, it would be great if it's only those two methods. But I'm not sure why there should be a NIE - I'm only subclassing a BinaryReader, so all other methods are untouched (but they should _hopefully_ use the updates ReadByte/ReadBytes methods). – mafu Mar 23 '15 at 14:37
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/73590/discussion-between-xanatos-and-mafu). – xanatos Mar 23 '15 at 14:37
  • Correction: I found that ReadByte should not be overwritten, as it internally calls Read. (Luaan got that right) – mafu Mar 24 '15 at 12:42
  • @mafu The error was mine, in general the ReadByte() should call `ReadStream.ReadByte` and not `base.ReadByte`. I'm not sure all streams call from ReadByte the Read(byte[]). Corrected in last edit – xanatos Mar 24 '15 at 13:23
1

Streams are meant to be encapsulated, not overriden:

public class DuploStream : Stream
{
    private readonly Stream _underlyingStream;
    private readonly Stream _copyStream;

    public DuploStream(Stream underlyingStream, Stream copyStream)
    {
        _underlyingStream = underlyingStream;
        _copyStream = copyStream;
    }

    public override bool CanRead { get { return true; } }
    public override bool CanSeek { get { return false; } }
    public override bool CanWrite { get { return false; } }

    public override void Flush()
    {
        _underlyingStream.Flush();
    }

    public override long Length { get { throw new NotSupportedException(); } }
    public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } }

    public override int Read(byte[] buffer, int offset, int count)
    {
        var bytesRead = _underlyingStream.Read(buffer, offset, count);

        _copyStream.Write(buffer, offset, bytesRead);

        return bytesRead;
    }

    public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
    public override void SetLength(long value) { throw new NotSupportedException(); }
    public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
}

Then, instead of creating BinaryReader on top of your underlying stream, you build it on top of DuploStream, passing the underlying stream and the copy stream.

Luaan
  • 62,244
  • 7
  • 97
  • 116