一.背景說明
工作中經常遇到解析二進制文件,一般協議是由甲乙雙方共同制定。因爲項目週期長,變更總是無法避免;所以初始設計與實現可能存在偏差。
1.統一的編碼格式(ASCII 、Unicode、UTF8),未必統一
2.不同語言,基本數據類型所佔長度(int32、int64等),接收語言可變
3.同一個地址值可變
二.如何應對
爲了應對這樣的不斷迭代調整,需要配置設計靈活。解析結果往往是基本類型或者是一個對象,我以泛型代替
public class UnitModel<T>where T:new()
{
public int startAdr { get; set; } //起地址
public int index { get; set; } //位號
public int length { get; set; } //字節長度
public string order { get; set; } //順序 "ABCD" "DCBA"
public string orderBit { get; set; } //順序 "AB" "BA"
public string type { get; set; } //類型
public string codetype { get; set; } //編碼格式
public T value { get; set; } //值
}
一個地址的解析通過上方來完成,本想寫成枚舉類型。
public class Decompose<T>
{
public UnitModel<T> Analyse(Byte[] bytes, UnitModel<T> t)
{
Byte[] bs = bytes.Skip(t.startAdr).Take(t.length).ToArray();
bs = Order(bs, t);//字節順序
bs = OrderBit(bs, t);//字內bit順序
return Return(bs,t); //字符串區分編碼,類型不區分
}
private UnitModel<T> Return(Byte[] bs, UnitModel<T> t)
{
switch (t.type)
{
case "bit":
t.value = ReturnBit(bs, t);
return t;
case "int":
t.value= ReturnInt32(bs, t);
return t;
case "string":
t.value= ReturnString(bs, t);
return t;
case "double":
t.value = ReturnDouble(bs, t);
return t;
case "float":
t.value = ReturnDouble(bs, t);
return t;
}
return t;
}
private string ReturnString(byte[] bs, UnitModel<T> t)
{
switch (t.codetype)
{
case "ASCII":
return Encoding.ASCII.GetString(bs);
case "Unicode":
return Encoding.Unicode.GetString(bs);
case "UTF8":
return Encoding.UTF8.GetString(bs);
}
return "";
}
/// <summary>
/// 返回位
/// </summary>
/// <param name="bs"></param>
/// <param name="t"></param>
/// <returns></returns>
private bool ReturnBit(Byte[] bs, UnitModel<T> t)
{
string s ="";
for (int i = 0;i < bs.Length; i++)
{
s += System.Convert.ToString(bs[i], 2).PadLeft(8, '0'); //!-- 先轉換成二進制
}
if(s[t.index]==0)
{
return false;
}
return true;
}
/// <summary>
/// 返回 int型
/// </summary>
/// <param name="bs"></param>
/// <param name="t"></param>
/// <returns></returns>
private int ReturnInt32(Byte[] bs, UnitModel<T> t)
{
switch (t.length)
{
case 4:
return BitConverter.ToInt32(bs, 0);
case 2:
return ReturnInt32MakeUp(bs, t);
case 1:
return ReturnInt32MakeUp(bs, t);
}
return 0;
}
/// <summary>
/// 高位補O
/// </summary>
/// <param name="bs"></param>
/// <param name="t"></param>
/// <returns></returns>
private int ReturnInt32MakeUp(Byte[] bs, UnitModel<T> t)
{
byte[] byteArray = new byte[4];
for (int m =0; m < t.length; m++)
{
byteArray[m] = bs[m];
}
return BitConverter.ToInt32(byteArray, 0);
}
/// <summary>
/// 暫無3個字節的處理方式
/// </summary>
/// <param name="bs"></param>
/// <param name="t"></param>
/// <returns></returns>
private double ReturnDouble(Byte[] bs, UnitModel<T> t)
{
switch (t.length)
{
case 8:
return BitConverter.ToDouble(bs, 0);
case 4:
return ReturnDoubleMakeUp(bs, t);
case 1:
return Convert.ToDouble(bs[0]);
}
return 0;
}
/// <summary>
/// float轉double
/// </summary>
/// <param name="bs"></param>
/// <param name="t"></param>
/// <returns></returns>
private double ReturnDoubleMakeUp(Byte[] bs, UnitModel<T> t)
{
return Convert.ToDouble( BitConverter.ToSingle (bs, 0));
}
/// <summary>
/// 高低位的問題
/// </summary>
/// <param name="bs"></param>
/// <param name="t"></param>
/// <returns></returns>
private Byte[] OrderBit(Byte[] bs, UnitModel<T> t)
{
switch (t.codetype)
{
case "AB":
return bs;
case "BA":
for (int i = 0; i < bs.Length; i++)
{
string s = System.Convert.ToString(bs[i], 2).PadLeft(8, '0'); //!-- 先轉換成二進制
string tmpS = "";
for (int j = s.Length - 1; j < 0; j--)
{
tmpS += s[j];
}
bs[i] = System.Convert.ToByte(tmpS, 2);
}
return bs;
}
return null;
}
/// <summary>
/// 順序問題
/// </summary>
/// <param name="bs"></param>
/// <param name="t"></param>
/// <returns></returns>
private Byte[] Order(Byte[] bs, UnitModel<T> t)
{
switch (t.order)
{
case "ABCD":
return bs;
case "DCBA":
Array.Reverse(bs);
return bs;
}
return null;
}
}
一個地址對應一個Model<T>,一個數據包分折成多個值。