C#文件方式读写结构体探析
2009-11-11 08:36
351 查看
最近一直在研究
.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
相同的内存位置。使用平台调用时,这种结构布局控制很有用。
-------------------------
我做了一个简单的测试程序,基本达成预定需求,不过程序该方式要求比较苛刻,如果要解析的数据与转换的结构体不匹配就会引发一系列莫名其妙的异常(如内存不可读等等之类),下面是测试程序的源代码,有兴趣的朋友可以看一看,也希望网友能提出更好的方案。
.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; } } }
相关文章推荐
- 探析C#文件方式读写结构体
- 结构体:探析C#文件方式读写结构体
- 结构体:探析C#文件方式读写结构体
- 结构体:探析C#文件方式读写结构体
- C#文件方式读写结构体探析
- c#读写文件的几种方式StreamReader、StreamWriter、FileStream
- C#的文件读写操作--流方式一次读写文件
- C#的文件共享读写方式
- C#读写快捷方式(.lnk文件)
- C#在文件读写结构体 Marshal效率低
- C# 文件的读写的几种方式
- C#读写文件总结
- c# winform 读写文件
- C#文件的读写
- c#实现数据流的处理和文件的读写机制(第七课)
- c#FileStream文件读写(转)
- c# 读写文件
- Android开发存储方式详解之本地文件读写实例
- Java IO读写大文件的几种方式及测试 (2012-04-21 22:58:06)
- C#txt文件读写基本操作