百度一下很多描述,下面先描述一下一些关键点。
字节序的优势,因为没有了关键字,所以体积非常小,并且等于是默认加密了。
字节序也是Google Protocol Buffer的基础理论。
- 在程序层面上来说 字节=byte。字节序其实就是一串byte,其实就是byte数组。其实按照名字,顾名思义,就有一个有顺序的字节数组。
- 然后大家都知道一般认为byte是最小单位。那么short占用2个byte,int占用4个byte,以此类推。那其实一个byte数组,实际上可以表示任何数据类型。
- 对于配置文件来说,常规做法会用excel,行列形式。每行都是唯一ID,加上后面的列的信息,每一列的含义其实是一样的。
如果一个配置文件的样子是:
怪物id | 模型名字 | 伤害 |
---|---|---|
1 | monster_fish | 10 |
2 | monster_dog | 100 |
那一般我们对应的代码里会是:
public class MonsterItem
{
public int nID = 0;
public string strModel = null;
public int nDamage = 0;
}
public Dictionary<int, MonsterItem> dic;
那么如果要序列化这么个结构,那么就可以理解成:
- int nID = 4个byte
- string strModel = char[字符串长度] = byte[字符串长度]
- 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 神一般的狄狄