天天看點

C#檔案方式讀寫結構體探析

    最近一直在研究.Net Micro Framework字型檔案(tinyfnt),由于tinyfnt檔案頭部有一段描述資料,是以很想定義一個結構體,像VC一樣直接從檔案中讀出來,省得用流一個個解析很是麻煩。

沒有想到在C#中竟沒有直接的指令,想必C#設計者認為提供了流和序列化技術,一切問題都可以迎刃而解了。

在C#中結構體是一個比較複雜的東西,在此之上有很多需要設定的參數,否則用起來就很容易出錯。下面是msdn上一段描述,看看也許有助于了解C#語言中的結構體。

-------------------------

通過使用屬性可以自定義結構在記憶體中的布局方式。例如,可以使用 StructLayout(LayoutKind.Explicit) 和 FieldOffset 屬性建立在 C/C++ 中稱為聯合的布局。

[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]

struct TestUnion

{

    [System.Runtime.InteropServices.FieldOffset(0)]

    public int i;

    public double d;

    public char c;

    public byte b;

}

在上一個代碼段中,TestUnion 的所有字段都從記憶體中的同一位置開始。

以下是字段從其他顯式設定的位置開始的另一個示例。

struct TestExplicit

    public long lg;

    public int i1;

    [System.Runtime.InteropServices.FieldOffset(4)]

    public int i2;

    [System.Runtime.InteropServices.FieldOffset(8)]

    [System.Runtime.InteropServices.FieldOffset(12)]

    [System.Runtime.InteropServices.FieldOffset(14)]

i1 和 i2 這兩個 int 字段共享與 lg 相同的記憶體位置。使用平台調用時,這種結構布局控制很有用。

我做了一個簡單的測試程式,基本達成預定需求,不過程式該方式要求比較苛刻,如果要解析的資料與轉換的結構體不比對就會引發一系列莫名其妙的異常(如記憶體不可讀等等之類),下面是測試程式的源代碼,有興趣的朋友可以看一看,也希望網友能提出更好的方案。

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.IO;

using System.Runtime.InteropServices;

namespace RWFile

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

        }

        //從檔案中讀結構體

        private void button1_Click(object sender, EventArgs e)

            string strFile = Application.StartupPath + "//test.dat";

            if (!File.Exists(strFile))

            {

                MessageBox.Show("檔案不存在");

                return;

            }

            FileStream fs = new FileStream(strFile, FileMode.Open, FileAccess.ReadWrite);

            TestStruct ts = new TestStruct();

            byte[] bytData = new byte[Marshal.SizeOf(ts)];

            fs.Read(bytData, 0, bytData.Length);

            fs.Close();

            ts = rawDeserialize(bytData);

            textBox1.Text = ts.dTest.ToString();

            textBox2.Text = ts.uTest.ToString();

            textBox3.Text = Encoding.Default.GetString(ts.bTest);

        //向檔案中寫結構體

        private void button2_Click(object sender, EventArgs e)

            FileStream fs = new FileStream(strFile, FileMode.Create , FileAccess.Write);

            ts.dTest = double.Parse(textBox1.Text);

            ts.uTest = UInt16.Parse(textBox2.Text);

            ts.bTest = Encoding.Default.GetBytes(textBox3.Text);

            byte[] bytData = rawSerialize(ts);

            fs.Write(bytData, 0, bytData.Length);

        [StructLayout(LayoutKind.Sequential,CharSet = CharSet.Ansi)] //,Size=16

        public struct TestStruct

            [MarshalAs(UnmanagedType.R8)] //,FieldOffset(0)] 

            public double dTest;

            [MarshalAs(UnmanagedType.U2)] //, FieldOffset(8)]

            public UInt16 uTest;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] //, FieldOffset(10)]

            public byte[] bTest;

        //序列化

        public static byte[] rawSerialize(object obj)

            int rawsize = Marshal.SizeOf(obj);

            IntPtr buffer = Marshal.AllocHGlobal(rawsize);

            Marshal.StructureToPtr(obj, buffer, false);

            byte[] rawdatas = new byte[rawsize];

            Marshal.Copy(buffer, rawdatas, 0, rawsize);

            Marshal.FreeHGlobal(buffer);

            return rawdatas;

        //反序列化

        public static TestStruct rawDeserialize(byte[] rawdatas)

            Type anytype = typeof(TestStruct);

            int rawsize = Marshal.SizeOf(anytype);

            if (rawsize > rawdatas.Length) return new TestStruct();

            Marshal.Copy(rawdatas, 0, buffer, rawsize);

            object retobj = Marshal.PtrToStructure(buffer, anytype);

            return (TestStruct)retobj;

        }      

      }