【问题标题】:How to read a data file generated by a C program from C#?如何从 C# 读取由 C 程序生成的数据文件?
【发布时间】:2014-02-12 15:09:02
【问题描述】:

我有一个定义了以下结构的 C 程序:

typedef struct{
   DWORD id;
   char  name[256];
}ROW;


typedef struct{
   DWORD        Prev;
   DWORD        Next;
   WORD         ItemCount;
   struct  ROW  Item[256];
} PAGE;

...有时程序会填充 PAGE 结构并将其写入磁盘文件。

现在,我需要从 C# 程序中读取此文件,但我不知道如何在 C# 中定义等效类,我尝试过的所有操作要么编译失败,要么引发异常。在 C 中,这是一项非常微不足道的任务,在 C# 中很可能也是如此,但是我是 C# 新手,还没有找到关于如何正确执行此操作的明确解释。

【问题讨论】:

  • 你知道结构是打包还是对齐。如果对齐,则在ItemCountItem 之间有两个字节的填充。我还怀疑存储到文件中的内容不一定包含所有 256 个ROW 项目。我猜它只会包含其中的ItemCount。我想说您需要在Marshal.PtrToStructureBinaryReader 之间进行选择。你喜欢哪个?你知道这些问题的答案吗?
  • 结构已打包,编译器设置为使用 1 字节结构对齐...文件确实包含所有 256 行,无论是否使用。我在用 BinaryReader 还没试过 Marshal.PtrToStruct...你是专家,哪种情况最适合这种情况?
  • 我想我会在这里使用BinaryReaderPtrToStructure 的原因涉及创建仅在读取文件时有用的结构类型。在您的 C# 代码中,您可能需要一个 Row 结构和一个包含 List<Row>Page 类。您希望我在此基础上向您展示一些大纲代码吗?

标签: c# c class struct


【解决方案1】:

我个人会在这里使用BinaryReader。原因是它允许您定义一组类型来表示这些结构,这在 C# 中感觉很干净。如果您要使用Marshal.PtrToStructure,那么您将不得不定义在 C# 中感觉很笨重的互操作类型。

您声明结构已打包,这很好。对齐的结构使生活变得更加困难,因为您必须了解编译器如何布置结构。对于存储到磁盘,通常应避免对齐结构。

我可能会定义这样的类型:

public struct Row
{
    public uint id;
    public string name;
}

public struct Page
{
    public uint prev;
    public uint next;
    public Row[] items;
}

如果您愿意,可以使用类。或List<Row>。这完全取决于你。

然后用这样的方法读取文件:

public static Page ReadPage(BinaryReader reader)
{
    Page page;
    page.prev = reader.ReadUInt32();
    page.next = reader.ReadUInt32();
    ushort count = reader.ReadUInt16();
    page.items = new Row[count];
    for (int i=0; i<count; i++)
    {
        page.items[i].id = reader.ReadUInt32();
        page.items[i].name = Encoding.ASCII.GetString(reader.ReadBytes(256));
    }
    // skip past the unused rows
    reader.ReadBytes((256+sizeof(uint))*(256-count)); 
}

我假设是 ASCII 编码。但也许它是 UTF-8 或 ANSI。我假设字符串是空终止的,但实际上没有编码任何空终止检测。我希望你能做到!

【讨论】:

  • 你的代码很好,很干净,就我的研究而言,这似乎是在 C# 中最自然的方式,但是像我一样曾经认为作为 C 程序员,不会这种方法不会导致严重的性能问题???我需要对每块数据执行 5 到 515 次读取之间的任意次数……而且该文件中有很多内容。
  • @georgeb .net 框架在这方面设计得很好。不,您不想一次发出大量 4 个字节的 I/O 指令。但这可以通过缓冲解决,在BinaryReader 之外完成。例如,您将使用 FileStream 从文件中读取。创建文件流对象时,请指定合适的缓冲区大小。这种关注点分离是非常可取的。您不想在解码结构化文件时担心缓冲。这是一个单独的问题。
  • 谢谢,你的回答似乎是最好的......我会根据它工作,让你知道会发生什么。
【解决方案2】:

请注意,这在 C 中是有风险的——你不能保证结构不会被额外的字节填充。 (嗯,不是没有一些特定于编译器的标志。)

StructLayout 属性可让您定义结构的布局方式:

[StructLayout(LayoutKind.Sequential)]
public struct Point 
{
   public int x;
   public int y;
}   

这将放置x,然后是y。如果你想强制字节对齐,你可以这样做:

[StructLayout(LayoutKind.Sequential,Pack=1)]
public struct Point 
{
   public int x;
   public int y;
}   

(在这种情况下无关紧要,但对于其他项目可能)。您还可以定义每个字段的精确布局:

[StructLayout(LayoutKind.Explicit)]
public struct Rect 
{
   [FieldOffset(0)] public int left;
   [FieldOffset(4)] public int top;
   [FieldOffset(8)] public int right;
   [FieldOffset(12)] public int bottom;
} 

(来自 MSDN 的示例:http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute%28v=vs.110%29.aspx

然后您应该能够使用Marshal.PtrToStructure 读取原始字节并将结构覆盖在其之上。我手头没有示例代码(我已经做了几年了),但它确实有效。

【讨论】:

    【解决方案3】:

    您必须使用 [StructLayout-Attribute] (http://msdn.microsoft.com/de-de/library/system.runtime.interopservices.structlayoutattribute%28v=vs.110%29.aspx) 例如:

    [StructLayout(LayoutKind.Sequential, Pack = ?, Size = ??)]
    public struct SomeStruct 
    {
      public ushort wYear; 
      public ushort second;
      public ushort third; 
      ...
    

    }

    有一天我做了一个通用的静态方法来将流读入结构:

     public static T StreamToStruct<T>(Stream stream, int offset = 0) where T : struct
        {
            Type structType = typeof(T);
            if(structType.StructLayoutAttribute.Value != LayoutKind.Sequential)
            {
                throw new ArgumentException("structType isn't a struct or the layout isn't sequential.");
            } 
    
            int tmpSize = Marshal.SizeOf(structType);
    
            Byte[] tmp = new Byte[tmpSize];
            stream.Read(tmp, offset, tmpSize);
    
            GCHandle structHandle = GCHandle.Alloc(tmp, GCHandleType.Pinned);
            try
            {
                T structure = (T)Marshal.PtrToStructure(structHandle.AddrOfPinnedObject(), structType);
                return structure;
            }
            catch(Exception)
            {
                throw;
            }
            finally
            {
                structHandle.Free();
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      • 2016-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-12
      相关资源
      最近更新 更多