0
我正在做一個項目,我需要將結構化數據寫入二進制文件。首先,我需要編寫一個頭文件,然後從某處獲取數據,填充並將結構化數據塊寫入到所述文件中。我移植C的結構爲C#如下:使用C#將連續結構化數據寫入二進制文件
C頭的結構:
typedef struct
{
DWORD uSignature;
DWORD uRecordLength;
} Header;
C數據結構:
typedef struct
{
DWORD uCode; // a two character identifier
char uLabel[10];
int uDate;
float uData[37];
} MyData;
這裏是C#頭結構:
struct Header
{
public uint uSignature;
public uint uRecordLength;
}
這裏是C#數據結構:
struct MyData
{
public MyData (int Count) : this()
{
uData = new Single[Count];
}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] uCode;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] uLabel;
public int uDate;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 37)]
public Single [] uData;
}
如果數據格式正確,該文件將由另一個可從二進制文件讀取的應用程序讀取。我打印出兩種結構類型的大小,它們看起來不錯。但是,輸出文件不能被所述應用程序讀取。
所以我有兩個問題:
是我正確地使用C到C#轉換的數據類型和法警?
我使用FileStream和BinaryWriter來寫入二進制文件。所有數據(標題和後續數據)必須按順序(連續)。正如我在飛行中創建和寫入數據結構,我不知道如何使用類似分配連續的內存:
public static byte[] GetData(object obj)
{
var size = Marshal.SizeOf(obj.GetType());
var data = new byte[size];
IntPtr pnt = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(obj, pnt, true);
// Copy the array to unmanaged memory.
Marshal.Copy(pnt, data, 0, size);
return data;
}
finally
{
// Free the unmanaged memory.
Marshal.FreeHGlobal(pnt);
}
}
任何幫助將不勝感激!
[編輯] 我添加了兩種方法來具體的結構數據轉換成字節數組,但文件仍然不能讀:
private byte[] DataToByteArray(MyData data)
{
int len = 0;
var size = Marshal.SizeOf(data.GetType());
var barray = new byte[size];
data.uCode.CopyTo(barray, 0);
len += data.uCode.Length;
data.uLabel.CopyTo(barray, len);
len += data.uLabel.Length;
BitConverter.GetBytes(0).CopyTo(barray, len);
len += data.uData.Length;
Buffer.BlockCopy(data.uData, 0, barray, len, data.uData.Length);
return barray;
}
private byte[] HeadToByteArray(Header data)
{
var size = Marshal.SizeOf(data.GetType());
var barray = new byte[size];
BitConverter.GetBytes(data.uSignature).CopyTo(barray, 0);
BitConverter.GetBytes(data.uRecordLength).CopyTo(barray, 4);
return barray;
}
【EDIT2】 這裏是如何工作在C:
#define NQ_EX 'QN'
FILE *fout;
fopen_s(&fout, "path_to_the_file", "wb");
Header head = { val1, sizeof(MyData) };
fwrite(&head, sizeof(Header), 1, fout);
while (!stop && data_is_coming)
{
MyData data;
memset(&data, 0, sizeof(data));
data.uCode = NQ_EX;
sprintf_s(data.uLabel, "%s", getVal("field1"));
data.uData[0] = getVal("field2");
data.uData[1] = getVal("field3");
....
fwrite(&data, sizeof(MyData), 1, fout);
}
你正在編寫C#,不是C.如果你不需要直接與同一進程中運行的C代碼的接口,你通常不希望打擾編組,因爲現在你有兩個問題(理解C做什麼,並以某種方式將其轉換爲C#)。如果你使用'BitConverter.GetBytes()'的順序調用,你只有一個問題(理解C的作用),並且這會減少對齊,字節順序和C處理字符串的方式('\ 0'終止)。 –
也許強制佈局是你錯過了什麼?即 [StructLayout(LayoutKind。顯式)] struct MyData {MyData(int Count) :this() { uData = new Single [Count]; } [FieldOffset(0)] [MarshalAs(UnmanagedType.ByValArray,SizeConst = 4)] public byte [] uCode; [FieldOffset(4)] [MarshalAs(UnmanagedType.ByValArray,SizeConst = 10)] public byte [] uLabel; etc ... –
很難誇大@Jeroen評論中提供的建議的重要性和有用性。嘗試通過p/invoke層來讀取和寫入流中的二進制數據真的很少或沒有理由。 .NET有可用的類,如'BinaryReader','BinaryWriter'和'BitConverter'來處理這種場景。它們更易於使用,併產生可靠的結果。坦率地說,即使在C/C++中,使用內存中的佈局來進行序列化也是一個壞主意;太脆弱了,明顯要序列化好得多。不要在C#中保留習慣的錯誤 –