C# 等效于 C 的 fread 文件 i/o

Posted

技术标签:

【中文标题】C# 等效于 C 的 fread 文件 i/o【英文标题】:A C# equivalent of C's fread file i/o 【发布时间】:2010-12-28 11:54:08 【问题描述】:

谁能告诉我如何在 C# .NET 版本 2 中以直接方式将字节数组放入结构中?就像 C 中熟悉的 fread 一样,到目前为止,我在读取字节流和自动填充结构方面还没有取得太大成功。我已经看到了一些使用 unsafe 关键字在托管代码中有指针 hocus-pocus 的实现。

看看这个示例:

public unsafe struct foobarStruct

   /* fields here... */

   public foobarStruct(int nFakeArgs)
      /* Initialize the fields... */
   

   public foobarStruct(byte[] data) : this(0) 
      unsafe 
         GCHandle hByteData = GCHandle.Alloc(data, GCHandleType.Pinned);
         IntPtr pByteData = hByteData.AddrOfPinnedObject();
         this = (foobarStruct)Marshal.PtrToStructure(pByteData, this.GetType());
         hByteData.Free();
      
   

foobarStruct 中有两个构造函数的原因

是否不能有空的构造函数。 在实例化结构时将一块内存(作为字节数组)传入构造函数。

该实现是否足够好,或者是否有更简洁的方法来实现这一目标?

编辑:我不想使用 ISerializable 接口或其实现。 我正在尝试读取二进制图像以计算出使用的字段并使用 PE 结构确定其数据。

【问题讨论】:

即使在 C 中,由于填充和对齐方面的考虑,直接将 fread 转换为 struct 也是一个非常糟糕的主意 您是否考虑过在这里使用序列化? 这个操作必须发生在一个不安全的块中,因为它是不安全的。结构可以包含指向引用类型等的成员。您要求获取磁盘的未知字节并将它们扔到可能包含指向任何内容的指针的结构中。要求框架验证您正在尝试做的事情太多了,因此是不安全的块。你仍然可以这样做,但框架必须采取“你自己”的方法。序列化为您处理潜在的问题,但它并不适合所有场景。我认为您不会比显示的代码做得更好。 感谢大家的意见!有一个快乐的季节问候/圣诞节:) 【参考方案1】:

使用 P/Invoke 编组器没有任何问题,它不是不安全的,您不必使用 unsafe 关键字。弄错只会产生错误的数据。它比显式编写反序列化代码更容易使用,尤其是当文件包含字符串时。您不能使用 BinaryReader.ReadString(),它假定字符串是由 BinaryWriter 写入的。但是请确保您使用 struct 声明来声明数据的结构,this.GetType() 不太可能正常工作。

这是一个通用类,可以使它适用于任何结构声明:

  class StructureReader<T> where T : struct 
    private byte[] mBuffer;
    public StructureReader() 
      mBuffer = new byte[Marshal.SizeOf(typeof(T))];
    
    public T Read(System.IO.FileStream fs) 
      int bytes = fs.Read(mBuffer, 0, mBuffer.Length);
      if (bytes == 0) throw new InvalidOperationException("End-of-file reached");
      if (bytes != mBuffer.Length) throw new ArgumentException("File contains bad data");
      T retval;
      GCHandle hdl = GCHandle.Alloc(mBuffer, GCHandleType.Pinned);
      try 
        retval = (T)Marshal.PtrToStructure(hdl.AddrOfPinnedObject(), typeof(T));
      
      finally 
        hdl.Free();
      
      return retval;
    

文件中数据结构的示例声明:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct Sample 
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)]
  public string someString;

您需要调整结构声明和属性以匹配文件中的数据。读取文件的示例代码:

  var data = new List<Sample>();
  var reader = new StructureReader<Sample>();
  using (var stream = new FileStream(@"c:\temp\test.bin", FileMode.Open, FileAccess.Read)) 
    while(stream.Position < stream.Length) 
      data.Add(reader.Read(stream));
    
  

【讨论】:

【参考方案2】:

您可能想要使用BinaryReader,它允许您以二进制形式读取原始类型。

byte[] 创建一个MemoryStream,然后从中使用BinaryReader。您应该能够读出结构并相应地填写您的对象。

【讨论】:

我意识到这是一篇旧帖子,但是对于那些在 2019 年或之后寻找答案的人来说,这正是我做这件事的方式。我在 BinaryReader 上创建了扩展方法,用于处理验证并用原始字节 [] 数据填充我的对象。如果您使用的是 .net core 2.1 或更高版本,您还可以使用 Span 代替字节数组。

以上是关于C# 等效于 C 的 fread 文件 i/o的主要内容,如果未能解决你的问题,请参考以下文章

C# 等效于 vcclr.h 中存在的 PtrToStringChars(String s) 函数

c语言中fread的用法

第九章学习笔记

C#语言进阶——2.C# 的 I/O 操作

《APUE》之文件系统篇

《APUE》之文件系统篇