【问题标题】:Preferred way to parse a custom binary flat file?解析自定义二进制平面文件的首选方法?
【发布时间】:2011-04-02 01:42:19
【问题描述】:

我有一个由 C 程序生成的平面文件。文件中的每条记录都包含一个固定长度的标头,后跟数据。标头包含一个指示后续数据大小的字段。我的最终目标是编写一个 C#/.NET 程序来查询这个平面文件,所以我正在寻找使用 C# 读取文件的最有效方法。

我无法在以下代码中找到与第 7 行等效的 .NET。据我所知,我必须发出多次读取(使用 BinaryReader 为标题的每个字段一个),然后发出一次读取以获取标题后面的数据。我正在尝试学习一种在两次读取操作中解析记录的方法(一次读取以获取固定长度的标头,第二次读取以获取以下数据)。

这是我尝试使用 C#/.NET 复制的 C 代码:

struct header header; /* 1-byte aligned structure (48 bytes) */
char *data;

FILE* fp = fopen("flatfile", "r");
while (!feof(fp))
{
  fread(&header, 48, 1, fp);
  /* Read header.length number of bytes to get the data. */
  data = (char*)malloc(header.length);
  fread(data, header.length, 1, fp);
  /* Do stuff... */
  free(data);
}

这是头文件的C结构:

struct header
{
    char  id[2];
    char  toname[12];
    char  fromname[12];
    char  routeto[6];
    char  routefrom[6];
    char  flag1;
    char  flag2;
    char  flag3;
    char  flag4;
    char  cycl[4];
    unsigned short len;
};

我想出了这个 C# 对象来表示 C 标头:

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi, Size = 48)]
class RouterHeader
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
    char[] Type;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
    char[] To;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
    char[] From;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    char[] RouteTo;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    char[] RouteFrom;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    char[] Flags;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    char[] Cycle;

    UInt16 Length;
}

【问题讨论】:

标签: c# parsing flat-file


【解决方案1】:

作为替代方案,您可以尝试使用类似联合的结构来创建可以一次性读取的标头结构(例如,作为适当长度的字符串),但随后能够在以下情况下引用各个字段你是来自那个结构的信息。

您可以找到更多关于使用 StructLayouts 和 FieldOffsets 来实现此类事情的详细信息here

还有一些关于使用 C#here 读写二进制文件的进一步讨论。建议使用 BinaryReader 读取多个字段通常对于少量(

【讨论】:

    【解决方案2】:

    好吧,您可以使用一次调用 Stream.Read 来读取长度(尽管您需要检查返回值以确保您已阅读了您所要求的所有内容;您可能无法一口气全部完成) 然后再次调用Stream.Read 将数据本身放入一个字节数组中(同样,循环直到你读到任何东西)。一旦它全部在内存中,您就可以从缓冲区中挑选出适当的字节来创建您的结构(或类)的实例。

    就我个人而言,我更喜欢明确地执行所有这些操作,而不是使用 StructLayout - 后者总是让我觉得有些脆弱。

    【讨论】:

      【解决方案3】:

      我建议您只编写代码(每个字段一个语句),逐个读取字段。这是一些额外的代码,但提供了更多的灵活性。首先,它使您不再需要内存中的数据结构必须与磁盘上的文件具有相同的布局。它可能是另一个结构的一部分,例如,您可以使用String 代替char[]

      另外考虑:如果你需要写一个 2.0 版本,在结构的末尾添加一个新字段怎么办?在您的示例中,您需要定义一个新结构,并且您将被这两个定义所困扰。如果您选择代码中的读/写,则可以通过有条件地读取新元素来使用相同的代码支持两者。

      【讨论】:

        【解决方案4】:

        我倾向于将数据读入一个数组,然后适当地组装数据对象,使用移位和加法来处理单词、长字等。我有一些实用程序类来处理这类事情。

        【讨论】:

          【解决方案5】:

          link Hans Passant provided 有答案。我会给他功劳,但我不知道该怎么做,因为他是作为评论而不是答案发布的。

          【讨论】:

            猜你喜欢
            • 2015-06-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-03-27
            • 2021-05-19
            • 1970-01-01
            相关资源
            最近更新 更多