【问题标题】:What's the 'best' way to parse a stream into a struct or class?将流解析为结构或类的“最佳”方法是什么?
【发布时间】:2009-02-12 07:39:01
【问题描述】:

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

Given 是一个串行连接(或者更抽象地说:一个流),您可以在其中接收一些数据,这些数据的格式如下:

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

使用传入数据的简单映射无济于事,因为您并不真正知道一个数据包的长度,因为它是写在结构中的(DataLength)。

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

让我们假设这些问题已经解决,并且我有某种本地缓冲区,它保存了流中的所有原始字节。通过给定的结构来解释它的最好方法是什么?只是做某种 for- 或 foreach-loop 还是存在一种更智能(性能更好)的方式(例如,使用 regex 或 linq)?

最好的问候, 奥利弗

【问题讨论】:

    标签: c#


    【解决方案1】:

    怎么样...

    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;
        }
    }
    

    请注意:我没有故意使用 BinaryReader.ReadString(),因为它旨在操作 BinaryWriter.WriteString() 生成的字符串。编码有点不同,尽管它是一个长度前缀字符串。

    【讨论】:

    • 我可能会添加 IDisposable,并可能将其称为 ReadPacket - 但这是一个很好的答案,+1
    • 但是为什么这里需要 IDisposable 呢?我没有使用任何非托管资源,为什么不让垃圾收集器为我做呢?
    • "非托管资源" 将是终结器。 IDisposable 是因为您正在封装/表示 IDisposable 的 else - Stream 和 BinaryReader。你的 Dispose() 应该调用 _reader.Dispose();
    • 看起来不错。正如已经提到的,需要一些错误处理,但我想我会试一试。
    • @Marc Gravell:这个流一开始不是我创建的,在我的课堂上关闭它会违反直觉。
    【解决方案2】:

    我会将它们存储在一个字节数组中并从那里重新创建它们,这是一种快速而简单的方法!

    我会读取字节并使用 BitConverter、Encoding.UTF8 进行转换..

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 抱歉,序列化不起作用,因为我只是数据的接收者,无法更改布局以使序列化工作。
      【解决方案4】:

      另一个可能的选择,利用 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
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-21
        • 2020-04-10
        • 1970-01-01
        • 2019-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多