Предпочитаемый способ разбора пользовательского двоичного плоского файла? - PullRequest
6 голосов
/ 21 августа 2010

У меня есть плоский файл, созданный программой на Си.Каждая запись в файле состоит из заголовка фиксированной длины, за которым следуют данные.Заголовок содержит поле, указывающее размер следующих данных.Моя конечная цель - написать программу на C # / .NET для запроса этого плоского файла, поэтому я ищу наиболее эффективный способ чтения файла с использованием C #.

У меня проблемы с поиском .NET-эквивалента строки 7 в следующем коде.Насколько я могу судить, мне нужно выполнить несколько операций чтения (по одному для каждого поля заголовка с помощью BinaryReader), а затем выполнить одно чтение, чтобы получить данные, следующие за заголовком.Я пытаюсь научиться анализировать запись в двух операциях чтения (одна - для получения заголовка фиксированной длины, а вторая - для получения следующих данных).

Это код C, который я пытаюсьдля копирования с использованием C # /. NET:

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 ]

2 голосов
/ 21 августа 2010

Что ж, вы можете использовать один вызов Stream.Read, чтобы прочитать длину (хотя вам нужно проверить возвращаемое значение, чтобы убедиться, что вы прочитали все, что просили; вы можете не получить все это за один раз), а затем еще один вызов Stream.Read, чтобы получить сами данные в байтовом массиве (опять же, зацикливание, пока вы ничего не прочитаете).Как только все в памяти, вы можете выбрать соответствующие байты из буфера, чтобы создать экземпляр вашей структуры (или класса).

Лично я предпочитаю делать все это явно, а не использовать StructLayout -последний всегда кажется мне несколько ломким.

0 голосов
/ 25 августа 2010

Ссылка , предоставленная Гансом Пассантом , содержит ответ. Я хотел бы отдать ему должное, но я не уверен, что делать, так как он опубликовал комментарий, а не ответ.

0 голосов
/ 22 августа 2010

Я бы хотел прочитать данные в массив, а затем соответствующим образом собрать объект данных, используя сдвиги и добавления для обработки слов, длинных слов и т. Д. У меня есть несколько служебных классов для обработки подобных вещей.

0 голосов
/ 21 августа 2010

Я бы порекомендовал вам просто написать код (один оператор на поле), который читает поля по одному.Это немного дополнительный код, но дает больше гибкости.Начнем с того, что оно освобождает вас от требования, что ваша структура данных в памяти должна иметь ту же структуру, что и файл на диске.Это может быть часть другой структуры, например, вы можете использовать String вместо char[].

Также учтите: что если вам нужно написать версию 2.0, в которой новое поле добавляется в конце структуры?В вашем примере вам нужно будет определить новую структуру, и вы будете придерживаться обоих определений.Если вы выбираете код для чтения / записи в коде, вы можете поддерживать оба с одним и тем же кодом, условно читая новый элемент.

0 голосов
/ 21 августа 2010

В качестве альтернативы, вы можете попробовать использовать структуру, подобную объединению, чтобы создать структуру заголовка, которую вы можете прочитать за один раз (например, в виде строки соответствующей длины), но затем сможете ссылаться на отдельные поля, когда вы информация из этой структуры.

Вы можете найти более подробную информацию об использовании StructLayouts и FieldOffsets для достижения такого рода вещи здесь .

Здесь есть дальнейшее обсуждение чтения и записи бинарных файлов с C # здесь . Предполагается, что использование BinaryReader для чтения в нескольких полях обычно более эффективно для небольшого (<40) числа полей. </p>

...