解析自定义二进制平面文件的首选方法?

时间:2010-08-21 15:40:28

标签: c# parsing flat-file

我有一个由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;
}

5 个答案:

答案 0 :(得分:2)

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

就我个人而言,我更愿意明确地做这一切,而不是使用StructLayout - 后者对我来说总觉得有些脆弱。

答案 1 :(得分:0)

作为替代方案,您可以尝试使用类似union的结构来创建一个可以一次读取的头结构(例如,作为适当长度的String),但是然后能够引用各个字段你是该结构的信息。

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

关于阅读和阅读的进一步讨论。用C#here编写二进制文件。建议使用BinaryReader读取多个字段对于小(<40)个字段通常更有效。

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

我的倾向是将数据读入数组,然后适当地组装数据对象,使用移位并添加处理单词,longwords等。我有一些实用程序类来处理这类事情。

答案 4 :(得分:0)

link Hans Passant provided有答案。我会赞美他,但我不知道该怎么办,因为他发表评论而不是回答。