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

简介:

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

没有想到在C#中竟没有直接的指令,想必C#设计者认为提供了流和序列化技术,一切问题都可以迎刃而解了。

在C#中结构体是一个比较复杂的东西,在此之上有很多需要设置的参数,否则用起来就很容易出错。下面是msdn上一段描述,看看也许有助于理解C#语言中的结构体。

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

通过使用属性可以自定义结构在内存中的布局方式。例如,可以使用 StructLayout(LayoutKind.Explicit) 和 FieldOffset 属性创建在 C/C++ 中称为联合的布局。

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

 
  1. struct TestUnion  
  2.  
  3. {  
  4.  
  5.     [System.Runtime.InteropServices.FieldOffset(0)]  
  6.  
  7.     public int i;  
  8.  
  9.     [System.Runtime.InteropServices.FieldOffset(0)]  
  10.  
  11.     public double d;  
  12.  
  13.     [System.Runtime.InteropServices.FieldOffset(0)]  
  14.  
  15.     public char c;  
  16.  
  17.     [System.Runtime.InteropServices.FieldOffset(0)]  
  18.  
  19.     public byte b;  
  20.  
  21. }  
  22.  

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

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

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

 
  1. struct TestExplicit  
  2.  
  3. {  
  4.  
  5.     [System.Runtime.InteropServices.FieldOffset(0)]  
  6.  
  7.     public long lg;  
  8.  
  9.     [System.Runtime.InteropServices.FieldOffset(0)]  
  10.  
  11.     public int i1;  
  12.  
  13.     [System.Runtime.InteropServices.FieldOffset(4)]  
  14.  
  15.     public int i2;  
  16.  
  17.     [System.Runtime.InteropServices.FieldOffset(8)]  
  18.  
  19.     public double d;  
  20.  
  21.     [System.Runtime.InteropServices.FieldOffset(12)]  
  22.  
  23.     public char c;  
  24.  
  25.     [System.Runtime.InteropServices.FieldOffset(14)]  
  26.  
  27.     public byte b;  
  28.  
  29. }  
  30.  

i1 和 i2 这两个 int 字段共享与 lg 相同的内存位置。使用平台调用时,这种结构布局控制很有用。

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

我做了一个简单的测试程序,基本达成预定需求,不过程序该方式要求比较苛刻,如果要解析的数据与转换的结构体不匹配就会引发一系列莫名其妙的异常(如内存不可读等等之类),下面是测试程序的源代码,有兴趣的朋友可以看一看,也希望网友能提出更好的方案。

 

 
  1. using System;  
  2.  
  3. using System.Collections.Generic;  
  4.  
  5. using System.ComponentModel;  
  6.  
  7. using System.Data;  
  8.  
  9. using System.Drawing;  
  10.  
  11. using System.Text;  
  12.  
  13. using System.Windows.Forms;  
  14.  
  15. using System.IO;  
  16.  
  17. using System.Runtime.InteropServices;  
  18.  
  19.    
  20.  
  21. namespace RWFile  
  22.  
  23. {  
  24.  
  25.     public partial class Form1 : Form  
  26.  
  27.     {  
  28.  
  29.         public Form1()  
  30.  
  31.         {  
  32.  
  33.             InitializeComponent();  
  34.  
  35.         }  
  36.  
  37.         //从文件中读结构体  
  38.  
  39.         private void button1_Click(object sender, EventArgs e)  
  40.  
  41.         {  
  42.  
  43.             string strFile = Application.StartupPath + "\\test.dat";  
  44.  
  45.             if (!File.Exists(strFile))  
  46.  
  47.             {  
  48.  
  49.                 MessageBox.Show("文件不存在");  
  50.  
  51.                 return;  
  52.  
  53.             }  
  54.  
  55.             FileStream fs = new FileStream(strFile, FileMode.Open, FileAccess.ReadWrite);  
  56.  
  57.             TestStruct ts = new TestStruct();  
  58.  
  59.             byte[] bytData = new byte[Marshal.SizeOf(ts)];  
  60.  
  61.             fs.Read(bytData, 0, bytData.Length);  
  62.  
  63.             fs.Close();  
  64.  
  65.             ts = rawDeserialize(bytData);  
  66.  
  67.             textBox1.Text = ts.dTest.ToString();  
  68.  
  69.             textBox2.Text = ts.uTest.ToString();  
  70.  
  71.             textBox3.Text = Encoding.Default.GetString(ts.bTest);   
  72.  
  73.         }  
  74.  
  75.         //向文件中写结构体  
  76.  
  77.         private void button2_Click(object sender, EventArgs e)  
  78.  
  79.         {  
  80.  
  81.             string strFile = Application.StartupPath + "\\test.dat";  
  82.  
  83.             FileStream fs = new FileStream(strFile, FileMode.Create , FileAccess.Write);  
  84.  
  85.             TestStruct ts = new TestStruct();  
  86.  
  87.             ts.dTest = double.Parse(textBox1.Text);  
  88.  
  89.             ts.uTest = UInt16.Parse(textBox2.Text);  
  90.  
  91.             ts.bTest = Encoding.Default.GetBytes(textBox3.Text);   
  92.  
  93.             byte[] bytData = rawSerialize(ts);  
  94.  
  95.             fs.Write(bytData, 0, bytData.Length);  
  96.  
  97.             fs.Close();  
  98.  
  99.         }  
  100.  
  101.    
  102.  
  103.         [StructLayout(LayoutKind.Sequential,CharSetCharSet = CharSet.Ansi)] //,Size=16 
  104.  
  105.         public struct TestStruct  
  106.  
  107.         {  
  108.  
  109.             [MarshalAs(UnmanagedType.R8)] //,FieldOffset(0)]    
  110.  
  111.             public double dTest;  
  112.  
  113.             [MarshalAs(UnmanagedType.U2)] //, FieldOffset(8)]  
  114.  
  115.             public UInt16 uTest;  
  116.  
  117.             [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] //, FieldOffset(10)]  
  118.  
  119.             public byte[] bTest;  
  120.  
  121.         }  
  122.  
  123.         //序列化  
  124.  
  125.         public static byte[] rawSerialize(object obj)  
  126.  
  127.         {  
  128.  
  129.             int rawsize = Marshal.SizeOf(obj);  
  130.  
  131.             IntPtr buffer = Marshal.AllocHGlobal(rawsize);  
  132.  
  133.             Marshal.StructureToPtr(obj, buffer, false);  
  134.  
  135.             byte[] rawdatas = new byte[rawsize];  
  136.  
  137.             Marshal.Copy(buffer, rawdatas, 0, rawsize);  
  138.  
  139.             Marshal.FreeHGlobal(buffer);  
  140.  
  141.             return rawdatas;  
  142.  
  143.         }  
  144.  
  145.         //反序列化  
  146.  
  147.         public static TestStruct rawDeserialize(byte[] rawdatas)  
  148.  
  149.         {  
  150.  
  151.             Type anytype = typeof(TestStruct);  
  152.  
  153.             int rawsize = Marshal.SizeOf(anytype);  
  154.  
  155.             if (rawsize > rawdatas.Length) return new TestStruct();  
  156.  
  157.             IntPtr buffer = Marshal.AllocHGlobal(rawsize);  
  158.  
  159.             Marshal.Copy(rawdatas, 0, buffer, rawsize);  
  160.  
  161.             object retobj = Marshal.PtrToStructure(buffer, anytype);  
  162.  
  163.             Marshal.FreeHGlobal(buffer);  
  164.  
  165.             return (TestStruct)retobj;  
  166.  
  167.         }         
  168.  
  169.       }  
  170.  
  171. }  
  172.  
  173.  
  174.  

 


















本文转自yefanqiu51CTO博客,原文链接:http://blog.51cto.com/yfsoft/323422,如需转载请自行联系原作者

相关文章
|
2月前
|
存储
文件操作(下)(想要了解如何操作文件,那么看这一片就足够了!)
文件操作(下)(想要了解如何操作文件,那么看这一片就足够了!)
文件操作(下)(想要了解如何操作文件,那么看这一片就足够了!)
|
29天前
|
存储 文件存储 对象存储
云计算存储问题之文件、块和对象的接口协议不一样如何解决
云计算存储问题之文件、块和对象的接口协议不一样如何解决
|
2月前
|
存储 编译器 Windows
文件操作(上)(想要了解如何操作文件,那么看这一片就足够了!)
文件操作(上)(想要了解如何操作文件,那么看这一片就足够了!)
|
9月前
|
C++
《C++避坑神器·七》二进制读写自定义类型导致崩溃或数据读写不全问题
《C++避坑神器·七》二进制读写自定义类型导致崩溃或数据读写不全问题
68 0
|
存储 C语言
内存的读写过程、现实模型及指针
内存的读写过程、现实模型及指针
130 0
内存的读写过程、现实模型及指针
|
Python
谈一谈|如何随意的对文件进行读写?
谈一谈|如何随意的对文件进行读写?
93 0
|
存储 Java
了解IO,内外存,文件操作这一篇就够了
了解IO,内外存,文件操作这一篇就够了
127 0
了解IO,内外存,文件操作这一篇就够了
|
C语言 索引
C语言 文件读写综合案例:读取LOL信息
C语言 文件读写综合案例:读取LOL信息
307 0
C语言 文件读写综合案例:读取LOL信息
|
C语言
【C 语言】文件操作 ( C 语言中的文件操作函数 | 磁盘与内存缓冲区 | 缓冲区工作机制 )
【C 语言】文件操作 ( C 语言中的文件操作函数 | 磁盘与内存缓冲区 | 缓冲区工作机制 )
148 0