C#文件方式读写结构体探析

简介: 最近一直在研究.Net Micro Framework字体文件(tinyfnt),由于tinyfnt文件头部有一段描述数据,所以很想定义一个结构体,像VC一样直接从文件中读出来,省得用流一个个解析很是麻烦。

最近一直在研究.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;

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

    public double d;

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

    public char c;

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

    public byte b;

}

在上一个代码段中,TestUnion 的所有字段都从内存中的同一位置开始。

以下是字段从其他显式设置的位置开始的另一个示例。

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

struct TestExplicit

{

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

    public long lg;

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

    public int i1;

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

    public int i2;

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

    public double d;

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

    public char c;

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

    public byte b;

}

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)

        {

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

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

            TestStruct ts = new TestStruct();

            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);

            fs.Close();

        }

 

        [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();

            IntPtr buffer = Marshal.AllocHGlobal(rawsize);

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

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

            Marshal.FreeHGlobal(buffer);

            return (TestStruct)retobj;

        }      

      }

}
相关文章
|
1月前
|
存储 C#
【C#】大批量判断文件是否存在的两种方法效率对比
【C#】大批量判断文件是否存在的两种方法效率对比
42 1
|
1月前
|
XML 存储 缓存
C#使用XML文件的详解及示例
C#使用XML文件的详解及示例
96 0
|
3月前
|
监控 安全 C#
使用C#如何监控选定文件夹中文件的变动情况?
使用C#如何监控选定文件夹中文件的变动情况?
118 19
|
3月前
|
编译器 C# Windows
C#基础:手动编译一个.cs源代码文件并生成.exe可执行文件
通过上述步骤,应该能够高效准确地编译C#源代码并生成相应的可执行文件。此外,这一过程强调了对命令行编译器的理解,这在调试和自动化编译流程中是非常重要的。
294 2
|
3月前
|
文字识别 C# Python
使用C#将几个Excel文件合并去重分类
使用C#将几个Excel文件合并去重分类
32 3
|
3月前
|
C# 图形学 数据安全/隐私保护
Unity数据加密☀️ 二、使用Rider将C#代码生成DLL文件
Unity数据加密☀️ 二、使用Rider将C#代码生成DLL文件
|
3月前
|
C#
C# 写日志文件
C# 写日志文件
45 0
|
6月前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
202 3
|
21天前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
30 3
|
2月前
|
API C#
C# 一分钟浅谈:文件系统编程
在软件开发中,文件系统操作至关重要。本文将带你快速掌握C#中文件系统编程的基础知识,涵盖基本概念、常见问题及解决方法。文章详细介绍了`System.IO`命名空间下的关键类库,并通过示例代码展示了路径处理、异常处理、并发访问等技巧,还提供了异步API和流压缩等高级技巧,帮助你写出更健壮的代码。
45 2