我有一个具有以下变量的对象:
bool firstBool;
float firstFloat; (0.0 to 1.0)
float secondFloat (0.0 to 1.0)
int firstInt; (0 to 10,000)
我使用ToString方法获取可以通过网络发送的字符串。扩大规模我遇到了数据量问题。
字符串现在看起来像这样:
"false:1.0:1.0:10000"
这是19个字符,每个字节2个字节,所以38个字节
我知道我可以通过手动存储4个字节的数据来保存这个大小:
A|B|B|B|B|B|B|B
C|C|C|C|C|C|C|D
D|D|D|D|D|D|D|D
D|D|D|D|D|X|X|X
A = bool(0 or 1), B = int(0 to 128), C = int(0 to 128), D = int(0 to 16384), X = Leftover bits
float(0.0 to 1.0)
转换为int(0 to 128)
,因为我可以在另一端重建它们,而且准确性并不是非常重要。我一直在尝试使用BitArray和byte []将数据转换成二进制结构。
经过一些实验,我最终得到了这个序列化过程(我知道它需要清理和优化)
public byte[] Serialize() {
byte[] firstFloatBytes = BitConverter.GetBytes(Mathf.FloorToInt(firstFloat * 128)); //Convert the float to int from (0 to 128)
byte[] secondFloatBytes = BitConverter.GetBytes(Mathf.FloorToInt(secondFloat * 128)); //Convert the float to int from (0 to 128)
byte[] firstIntData = BitConverter.GetBytes(Mathf.FloorToInt(firstInt)); // Get the bytes for the int
BitArray data = new BitArray(32); // create the size 32 bitarray to hold all the data
int i = 0; // create the index value
data[i] = firstBool; // set the 0 bit
BitArray ffBits = new BitArray(firstFloatBytes);
for(i = 1; i < 8; i++) {
data[i] = ffBits[i-1]; // Set bits 1 to 7
}
BitArray sfBits = new BitArray(secondFloatBytes);
for(i = 8; i < 15; i++) {
data[i] = sfBits[i-8]; // Set bits 8 to 14
}
BitArray fiBits = new BitArray(firstIntData);
for(i = 15; i < 29; i++) {
data[i] = fiBits[i-15]; // Set bits 15 to 28
}
byte[] output = new byte[4]; // create a byte[] to hold the output
data.CopyTo(output,0); // Copy the bits to the byte[]
return output;
}
从这个结构中获取信息比将其转化为这种形式要复杂得多。我想我可以使用按位运算符和位掩码来锻炼一些东西。
事实证明这比我预期的要复杂得多。我认为访问byte []的位来直接操作数据,提取位的范围,然后转换回重建对象所需的值将非常容易。是否存在此类数据序列化的最佳实践?有谁知道我能阅读的教程或示例参考?
答案 0 :(得分:2)
标准和有效的序列化方法是:
使用BinaryWriter
/ BinaryReader
:
public byte[] Serialize()
{
using(var s = new MemoryStream())
using(var w = new BinaryWriter(s))
{
w.Write(firstBool);
w.Write(firstFloat);
...
return s.ToArray();
}
}
public void Deserialize(byte[] bytes)
{
using(var s = new MemoryStream(bytes))
using(var r = new BinaryReader(s))
{
firstBool = r.ReadBool();
firstFload = r.ReadFloat();
...
}
}
BinaryWriter
/ BinaryReader
要快得多(约7次)。 Protobuf更灵活,易于使用,非常受欢迎,序列化的字节数减少了约33%。 (当然这些数字是数量级的,取决于你序列化的内容和方式)。
现在基本上BinaryWriter
将写入1 + 4 + 4 + 4 = 13个字节。您可以通过将值转换为bool,byte,byte,short将其缩小为5个字节,方法是将其舍入为您想要的方式。最后,如果你真的想要,可以很容易地将bool与你的一个字节合并以获得4个字节。
我并不真的不鼓励手动序列化。但它在性能方面必须付出代价。代码非常难以理解。直接使用位掩码和二进制移位字节,但要尽可能简单。不要使用BitArray。它很慢而且不易读。
答案 1 :(得分:1)
这是打包/解包的简单方法。但是你没有将浮点数转换为仅7/8位的精度
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
foreach (Data data in Data.input)
{
Data.Print(data);
Data results = Data.Unpack(Data.Pack(data));
Data.Print(results);
}
Console.ReadLine();
}
}
public class Data
{
public static List<Data> input = new List<Data>() {
new Data() { firstBool = true, firstFloat = 0.2345F, secondFloat = 0.432F, firstInt = 12},
new Data() { firstBool = true, firstFloat = 0.3445F, secondFloat = 0.432F, firstInt = 11},
new Data() { firstBool = false, firstFloat = 0.2365F, secondFloat = 0.432F, firstInt = 9},
new Data() { firstBool = false, firstFloat = 0.545F, secondFloat = 0.432F, firstInt = 8},
new Data() { firstBool = true, firstFloat = 0.2367F, secondFloat = 0.432F, firstInt = 7}
};
public bool firstBool { get; set; }
public float firstFloat {get; set; } //(0.0 to 1.0)
public float secondFloat {get; set; } //(0.0 to 1.0)
public int firstInt { get; set; } //(0 to 10,000)
public static byte[] Pack(Data data)
{
byte[] results = new byte[4];
results[0] = (byte)((data.firstBool ? 0x80 : 0x00) | (byte)(data.firstFloat * 128));
results[1] = (byte)(data.secondFloat * 256);
results[2] = (byte)((data.firstInt >> 8) & 0xFF);
results[3] = (byte)(data.firstInt & 0xFF);
return results;
}
public static Data Unpack(byte[] data)
{
Data results = new Data();
results.firstBool = ((data[0] & 0x80) == 0) ? false : true;
results.firstFloat = ((float)(data[0] & 0x7F)) / 128.0F;
results.secondFloat = (float)data[1] / 256.0F;
results.firstInt = (data[2] << 8) | data[3];
return results;
}
public static void Print(Data data)
{
Console.WriteLine("Bool : '{0}', 1st Float : '{1}', 2nd Float : '{2}', Int : '{3}'",
data.firstBool,
data.firstFloat,
data.secondFloat,
data.firstInt
);
}
}
}