将流解析为结构或类的“最佳”方法是什么?

时间:2021-08-07 23:24:08

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

实际上我正在使用.Net Framework 3.5,所以我拥有所有这些不错的小功能,如lambdas,linq等。

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).

使用传入数据的简单映射无济于事,因为您实际上并不知道一个数据包将持续多长时间,因为它是在结构中写入的(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?

所以我的第一个想法是按字节读取流并将其放入???。是的,这是下一个问题。在哪里存储这个第一个原始数据?进入一个简单的字节数组,具有最大可能的长度(即65540字节,因为DataLength是一个UInt16加上来自其他字段的附加字节)。或者我应该打开一个队列并填充它所有传入的字节或可能存在另一个很好的可能性?

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)?

让我们假设这些问题被清除,我有一些本地缓冲区,它保存流中的所有原始字节。给定结构解释它的最好方法是什么?只是做某种for-or foreach-loop或存在更聪明(性能更好)的方式(例如使用regex或linq)?

Best regards, Oliver

最好的问候,奥利弗

4 个解决方案

#1


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.

请注意:我没有故意使用BinaryReader.ReadString(),因为它设计用于对BinaryWriter.WriteString()生成的字符串进行操作。即使它的长度为前缀字符串,编码也有点不同。

#2


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..

我会读取字节并使用BitConverter,Encoding.UTF8转换它们。

#3


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

检查一下,无论如何它归结为使用[Serializable]属性,你完成了http://www.ondotnet.com/pub/a/dotnet/2002/08/26/serialization.html

#4


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

另一个可能的选择,使用C#的yield关键字:

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
}

#1


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.

请注意:我没有故意使用BinaryReader.ReadString(),因为它设计用于对BinaryWriter.WriteString()生成的字符串进行操作。即使它的长度为前缀字符串,编码也有点不同。

#2


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..

我会读取字节并使用BitConverter,Encoding.UTF8转换它们。

#3


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

检查一下,无论如何它归结为使用[Serializable]属性,你完成了http://www.ondotnet.com/pub/a/dotnet/2002/08/26/serialization.html

#4


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

另一个可能的选择,使用C#的yield关键字:

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
}