字节序的序列化和反序列化,推导以及实现(unity c#)

百度一下很多描述,下面先描述一下一些关键点。
字节序的优势,因为没有了关键字,所以体积非常小,并且等于是默认加密了。
字节序也是Google Protocol Buffer的基础理论。

  1. 在程序层面上来说 字节=byte。字节序其实就是一串byte,其实就是byte数组。其实按照名字,顾名思义,就有一个有顺序的字节数组。
  2. 然后大家都知道一般认为byte是最小单位。那么short占用2个byte,int占用4个byte,以此类推。那其实一个byte数组,实际上可以表示任何数据类型。
  3. 对于配置文件来说,常规做法会用excel,行列形式。每行都是唯一ID,加上后面的列的信息,每一列的含义其实是一样的。

如果一个配置文件的样子是:

怪物id模型名字伤害
1monster_fish10
2monster_dog100

那一般我们对应的代码里会是:

public class MonsterItem
{
	public int nID = 0;
	public string strModel = null;
	public int nDamage = 0;
}

public Dictionary<int, MonsterItem> dic;

那么如果要序列化这么个结构,那么就可以理解成:

  1. int nID = 4个byte
  2. string strModel = char[字符串长度] = byte[字符串长度]
  3. int nDamage = 4个byte
  • 如果我假设这个字符串,长度是8,那么其实 4+8+4, 16个byte就能表示一行配置数据。
    但是这里有一个注意点,string在一串byte里面,我们不知道多长,就没办法解析出正确的string。所以保存string的时候,需要带上string长度,和网络数据包的包头一样的概念,长度一般习惯性会用ushort类型,就是2个字节,当然根据项目,也可以是byte,可能你的字符串不需要特别长。
  • 所以一行数据的长度就是 4+ (2+8) + 4, 18个字节
    那到这里其实就可以得出结论了,如果你用json,xml,或者其他的一些明文配置文件格式,根本没法达到这种程度,因为会存在标志符,或者关键字。
  • 单行的数据长度已知道,那只要重复行。组装成字符串数组。其实就是我们的配置文件的序列化了。只需要把这一串byte写进文本,那一个压缩的配置文件就完成了。其实对应写入,对应解析的轮廓也很清晰了,剩下就是实现了。

理论结束之后,开始实际操作。

一开始肯定是解析配置文件格式,xml 或者 csv 或者 txt?无所谓。自己解析成数据。不多说了。
然后就是怎么组装这个字节序。既然是个有顺序的byte数组,那首先是有一个数组

private List<byte> m_listBinary = new List<byte>();

之所以用List是因为 byte[] 初始化构建长度,万一越界。。着实不方便操作。List 可以ToArray().

		public void WriteByte(byte bValue)
		{
			m_listBinary.Add(bValue);
		}

		public void WriteBytes(byte[] nValue)
		{
			m_listBinary.AddRange(nValue);
		}

        public void WriteBool(bool bValue)
        {
            byte num = 0;
            if (bValue)
            {
                num = 1;
            }
            WriteByte(num);
        }
        
		public void WriteInt(int nValue)
		{
			byte[] bytes = BitConverter.GetBytes(nValue);
			if (BitConverter.IsLittleEndian)
			{
				bytes = TypeConvert.ReverseBytes(bytes);
			}

			WriteBytes(bytes);
		}

然后就可以类似如此,把你需要的数据类型转换成byte[] 加入我们上面写的那个List

public void WriteString(string strValue)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(strValue);
			if (BitConverter.IsLittleEndian)
			{
				bytes = TypeConvert.ReverseBytes(bytes);
			}

			int length = bytes.Length;
			if (length > 0)
			{
				WriteUShort((ushort)length);
				WriteBytes(bytes);
			}
			else
			{
				WriteUShort(0);
			}
		}

需要注意的是string的写入,先写长度,再写内容。

组装完所有内容写入文本就行了。
注:

if (BitConverter.IsLittleEndian)
{
	bytes = TypeConvert.ReverseBytes(bytes);
}

指的是大端小端序,可以百度一下。只是byte是顺序还是倒序,这个就根据自己的环境和需求来定义,其实不写也没啥问题。


然后就是一个比较标准的byte顺序读取工具类

using System;
using System.Text;
using UnityEngine;

namespace Fox.BinaryConfig
{
	public class BinaryReadBuffer : MemoryBuffer
	{
		private int m_nCurReadPos = 0;  //当前读取位置
		private int m_OriBufferLen = 0; //原始Buffer大小

		public BinaryReadBuffer(int len) : base(len)
		{
			m_OriBufferLen = len;
		}

		public BinaryReadBuffer(byte[] Buffer) : base(Buffer)
		{
			m_OriBufferLen = Buffer.Length;
		}

		public override void Clear()
		{
			base.Clear();

			m_nCurReadPos = 0;
		}

		public bool HasRemaining()
		{
			return (m_nBufferPos > m_nCurReadPos);
		}

		public int GetRemaining()
		{
			return (m_nBufferPos - m_nCurReadPos);
		}

		/// <summary>
		/// 设置读取游标位置
		/// </summary>
		public void SetReadPos(int value)
		{
			m_nCurReadPos = value;
			if (m_nCurReadPos < 0)
			{
				m_nCurReadPos = 0;
			}
			if (m_nCurReadPos > m_nBufferPos)
			{
				m_nCurReadPos = m_nBufferPos;
			}
		}

		public int GetReadPos()
		{
			return m_nCurReadPos;
		}

		public void Settle()
		{
			if (m_nBufferPos > 0)
			{
				if (m_nCurReadPos < m_nBufferPos)
				{
					Buffer.BlockCopy(m_Buffer, m_nCurReadPos, m_Buffer, 0, m_nBufferPos - m_nCurReadPos);
					m_nBufferPos = m_nBufferPos - m_nCurReadPos;
					m_nCurReadPos = 0;
					if (m_nBufferPos < 0)
					{
						m_nBufferPos = 0;
					}
				}
				else
				{
					Clear();
				}

				//重置容量
				if (m_nBufferPos < m_OriBufferLen && m_nBufferMaxLen > m_OriBufferLen)
				{
					byte[] buffer = new byte[m_OriBufferLen];
					Buffer.BlockCopy(m_Buffer, 0, buffer, 0, m_OriBufferLen);
					m_Buffer = buffer;
					m_nBufferMaxLen = m_OriBufferLen;
				}
			}
		}

		public bool ReadBool()
		{
			return (ReadByte() != 0);
		}

		public sbyte ReadsByte()
		{
			sbyte num = 0;
			try
			{
				num = (sbyte)m_Buffer[m_nCurReadPos];
				m_nCurReadPos++;
			}
			catch (ArgumentException exception)
			{
				Debug.LogException(exception);
			}

			return num;
		}

		public byte ReadByte()
		{
			byte num = 0;
			try
			{
				num = m_Buffer[m_nCurReadPos];
				m_nCurReadPos++;
			}
			catch (ArgumentException exception)
			{
				Debug.LogException(exception);
			}

			return num;
		}

		public float ReadFloat()
		{
			float num = 0f;
			try
			{
				if (BitConverter.IsLittleEndian)
				{
					byte[] bytes = new byte[4];
					Buffer.BlockCopy(m_Buffer, m_nCurReadPos, bytes, 0, 4);
					bytes = TypeConvert.ReverseBytes(bytes);

					num = BitConverter.ToSingle(bytes, 0);
				}
				else
				{
					num = BitConverter.ToSingle(m_Buffer, m_nCurReadPos);
				}
				m_nCurReadPos += 4;
			}
			catch (ArgumentException exception)
			{
				Debug.LogException(exception);
			}

			return num;
		}

		public int ReadInt()
		{
			int num = 0;
			try
			{
				if (BitConverter.IsLittleEndian)
				{
					byte[] bytes = new byte[4];
					Buffer.BlockCopy(m_Buffer, m_nCurReadPos, bytes, 0, 4);
					bytes = TypeConvert.ReverseBytes(bytes);

					num = BitConverter.ToInt32(bytes, 0);
				}
				else
				{
					num = BitConverter.ToInt32(m_Buffer, m_nCurReadPos);
				}
				m_nCurReadPos += 4;
			}
			catch (ArgumentException exception)
			{
				Debug.LogException(exception);
			}

			return num;
		}

		public long ReadInt64()
		{
			long num = 0L;
			try
			{
				if (BitConverter.IsLittleEndian)
				{
					byte[] bytes = new byte[8];
					Buffer.BlockCopy(m_Buffer, m_nCurReadPos, bytes, 0, 8);
					bytes = TypeConvert.ReverseBytes(bytes);

					num = BitConverter.ToInt64(bytes, 0);
				}
				else
				{
					num = BitConverter.ToInt64(m_Buffer, m_nCurReadPos);
				}
				m_nCurReadPos += 8;
			}
			catch (ArgumentException exception)
			{
				Debug.LogException(exception);
			}

			return num;
		}

		public short ReadShort()
		{
			short num = 0;
			try
			{
				if (BitConverter.IsLittleEndian)
				{
					byte[] bytes = new byte[2];
					Buffer.BlockCopy(m_Buffer, m_nCurReadPos, bytes, 0, 2);
					bytes = TypeConvert.ReverseBytes(bytes);

					num = BitConverter.ToInt16(bytes, 0);
				}
				else
				{
					num = BitConverter.ToInt16(m_Buffer, m_nCurReadPos);
				}
				m_nCurReadPos += 2;
			}
			catch (ArgumentException exception)
			{
				Debug.LogException(exception);
			}
			return num;
		}

		public uint ReadUInt()
		{
			uint num = 0;
			try
			{
				if (BitConverter.IsLittleEndian)
				{
					byte[] bytes = new byte[4];
					Buffer.BlockCopy(m_Buffer, m_nCurReadPos, bytes, 0, 4);
					bytes = TypeConvert.ReverseBytes(bytes);

					num = BitConverter.ToUInt32(bytes, 0);
				}
				else
				{
					num = BitConverter.ToUInt32(m_Buffer, m_nCurReadPos);
				}
				m_nCurReadPos += 4;
			}
			catch (ArgumentException exception)
			{
				Debug.LogException(exception);
			}
			return num;
		}

		public ushort ReadUShort()
		{
			ushort num = 0;
			try
			{
				if (BitConverter.IsLittleEndian)
				{
					byte[] bytes = new byte[2];
					Buffer.BlockCopy(m_Buffer, m_nCurReadPos, bytes, 0, 2);
					bytes = TypeConvert.ReverseBytes(bytes);

					num = BitConverter.ToUInt16(bytes, 0);
				}
				else
				{
					num = BitConverter.ToUInt16(m_Buffer, m_nCurReadPos);
				}
				m_nCurReadPos += 2;
			}
			catch (ArgumentException exception)
			{
				Debug.LogException(exception);
			}
			return num;
		}

		public string ReadString()
		{
			int count = 0;
			string str = null;
			try
			{
				count = ReadUShort();
				if (count >= 0)
				{
					if (BitConverter.IsLittleEndian)
					{
						byte[] bytes = new byte[count];
						Buffer.BlockCopy(m_Buffer, m_nCurReadPos, bytes, 0, count);
						bytes = TypeConvert.ReverseBytes(bytes);
						str = Encoding.UTF8.GetString(bytes, 0, count);
					}
					else
					{
						str = Encoding.UTF8.GetString(m_Buffer, m_nCurReadPos, count);
					}
					m_nCurReadPos += count;
				}
			}
			catch (ArgumentException exception)
			{
				Debug.LogException(exception);
			}

			return str;
		}

		public byte[] ReadBytes(int length)
		{
			byte[] dst = null;

			if (GetRemaining() >= length)
			{
				dst = new byte[length];

				Buffer.BlockCopy(m_Buffer, m_nCurReadPos, dst, 0, length);
				m_nCurReadPos += length;
			}

			return dst;
		}

		public byte[] ReadBytes()
		{
			byte[] dst = null;

			if (GetRemaining() >= 4)
			{
				int length = ReadInt();
				if ((length > 0) && (GetRemaining() >= length))
				{
					dst = new byte[length];

					Buffer.BlockCopy(m_Buffer, m_nCurReadPos, dst, 0, length);
					m_nCurReadPos += length;
				}
			}

			return dst;
		}
	}
}

程序学无止尽。
欢迎大家沟通,有啥不明确的,或者不对的,也可以和我私聊
我的QQ 334524067 神一般的狄狄

### 如何使用 Keil5 Hex 文件 对于仅拥有已编译好的 hex 文件而无源文件的情况,在 Keil V5 平台上直接hex 文件至单片机(如华大单片机)需采取特定的方法,因为直接调用该平台进行此类操作不可行[^1]。 #### 设置 Output 路径 进入 Keil 的 output 设置界面,指定要录的 hex 文件的具体位置。确保在路径输入框中填完整的 hex 文件名称并附带 `.hex` 扩展名;缺少此扩展名可能导致系统继续尝试录先前编译的结果而非所选的 hex 文件[^3]。 #### 配置 Flash 工具选项 针对不同类型的微控制器(MCU),可能还需调整 flash 下载工具的相关配置参数以匹配目标设备的要求。这一步骤通常涉及选择合适的编程算法以及设定通信接口等细节[^2]。 #### 启动下载过程 完成上述准备工作之后,可以通过点击调试窗口内的 “Download” 或者快捷菜单里的相应命令来启动实际的程序入流程。如果一切顺利的话,软件会自动连接硬件并将选定的 hex 数据传输到 MCU 中存储起来[^4]。 ```python # Python 示例代码用于说明自动化脚本概念 (并非真实实现) def download_hex_to_mcu(hex_file_path, mcu_type): """ 自定义函数模拟将 HEX 文件下载到指定型号的 MCU 上 参数: hex_file_path -- 完整路径字符串指向待上传的 .hex 文件 mcu_type -- 字符串表示的目标单片机类型标识符 返回值: 成功则返回 True ,失败抛出异常信息 """ try: configure_output_settings(hex_file_path) # 设定输出设置 select_flash_tool(mcu_type) # 挑选适合的闪存工具 execute_download_command() # 发送下载指令 return True # 表明成功结束 except Exception as e: raise RuntimeError(f"Failed to upload {hex_file_path}: {e}") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值