Oliver
Oliver

Reputation: 45119

What's the 'best' way to parse a stream into a struct or class?

actually i'm working with .Net Framework 3.5, so i have all these nice little features like lambdas, linq, etc.

Given is a serial connection (or to be more abstract: a stream) where you receive some data, which will be in a format like this:

struct Packet
{
    byte STX
    UInt16 DataLength
    string Data
    byte CRC
    byte ETX
}

Using a simple mapping of the incoming data doesn't help due to the fact, that you don't really know how long one packet will be, cause it's written within the structure (the DataLength).

So my first idea would be to read the stream by byte and put it into ???. Yes, that's the next question. Where to store this first Raw data? Into a simple byte array, with the maximum possible length (that would be 65540 bytes, due to the fact, that DataLength is an UInt16 plus the additional bytes from the other fields). Or should i open up a Queue and fill it up will all the incoming bytes or maybe exists there another nice possibility?

Let's assume these problems are cleared and i have some kind of local buffer, that holds all the raw bytes from the stream. What's the nicest way to interpret it by the given structure?? Just doing some kind of for- or foreach-loop or exists there a smarter (with better performance) way (e.g. with regex or linq)?

Best regards, Oliver

Upvotes: 1

Views: 6096

Answers (4)

Sandeep Datta
Sandeep Datta

Reputation: 29345

How about...

struct Packet
{
    public byte STX;
    public UInt16 DataLength;
    public string Data;
    public byte CRC;
    public byte ETX;
}

//Warning: Need to add error handling
class PacketReader
{
    private BinaryReader _reader;

    public PacketReader(Stream stream)
    {
        _reader = new BinaryReader(stream);
    }

    Packet ReadPacket()
    {
        var packet = new Packet() 
            {
                STX = _reader.ReadByte(),
                DataLength = _reader.ReadUInt16(),
                Data = Encoding.ASCII.GetString(
                    _reader.ReadBytes(packet.DataLength)),
                CRC = _reader.ReadByte(),
                ETX = _reader.ReadByte()
            };

        return packet;
    }
}

Please note: I have not used BinaryReader.ReadString() on purpose because it is designed to operate on strings generated by BinaryWriter.WriteString(). The encoding is a bit different even though its a length prefixed string.

Upvotes: 5

Aaron Schultz
Aaron Schultz

Reputation: 1234

Another possible option, making use of C#'s yield keyword:

public struct Packet    
{    
    public byte STX;    
    public UInt16 DataLength;    
    public string Data;    
    public byte CRC;    
    public byte ETX;    
}    

public static class StreamExtensions
{
    public IEnumerable<Packet> ToPacketStream(this Stream stream)
    {
        BinaryReader reader = new BinaryReader(stream);
        while(reader.PeekChar() != -1) //Optionally change this to reflect your exit conditions
        {
            var packet = new Packet();

            packet.STX =        _reader.ReadByte();       
            packet.DataLength = _reader.ReadUInt16();       
            packet.Data =       Encoding.ASCII.GetString(_reader.ReadBytes(packet.DataLength));       
            packet.CRC =        _reader.ReadByte();       
            packet.ETX =        _reader.ReadByte();

            yield return packet;
        }
    }   
}

//Usage
foreach(var packet in stream.ToPacketStream())
{
    //Handle packet
}

Upvotes: 0

Robert Gould
Robert Gould

Reputation: 69893

Check this out, anyways it boils down to using the [Serializable] attribute, and your done http://www.ondotnet.com/pub/a/dotnet/2002/08/26/serialization.html

Upvotes: 0

Peter
Peter

Reputation: 38535

I would store them in a byte array and recreate them from there, its a fast and simple way to do it!

I would read the bytes and convert them With BitConverter, Encoding.UTF8..

Upvotes: 0

Related Questions