Чтение в двоичном файле, содержащем неизвестное количество структур (C #) - PullRequest
1 голос
/ 16 августа 2010

Хорошо, поэтому у меня в настоящее время есть двоичный файл, содержащий неизвестное количество структур, подобных этому:

private struct sTestStruct
{
    public int numberOne;
    public int numberTwo;
    public int[] numbers; // This is ALWAYS 128 ints long.
    public bool trueFalse;
}

Пока что я использую следующее, чтобы прочитать все структуры в Список <>:

List<sTestStruct> structList = new List<sTestStruct>();

while (binReader.BaseStream.Position < binReader.BaseStream.Length)
{
    sTestStruct temp = new sTestStruct();
    temp.numberOne = binReader.ReadInt32();
    temp.numberTwo = binReader.ReadInt32();
    temp.numbers = new int[128];
    for (int i = 0; i < temp.numbers.Length; i++)
    {
        temp.numbers[i] = binReader.ReadInt32();
    }
    temp.trueFalse = binReader.ReadBoolean();

    // Add to List<>
    structList.Add(temp);
}

Я действительно не хочу этого делать, поскольку пользователю может отображаться только одна из структур, поэтому нет смысла считывать более одной записи за раз.Поэтому я подумал, что мог бы прочитать в конкретной записи что-то вроде:

fileStream.Seek(sizeof(sTestStruct) * index, SeekOrigin.Begin);

Но это не позволит мне, так как он не знает размер sTestStruct, структура не позволит мне заранее определить размер массиватак как же мне поступить об этом ??

Ответы [ 3 ]

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

sTestStruct не сохраняется в одной последовательной памяти, а sizeof(sTestStruct) не имеет прямого отношения к размеру записей в файле. numbers члены - это ссылка на массив, который вы выделяете в коде чтения.

Но вы можете легко указать размер записи в коде, так как это постоянное значение. Этот код будет искать запись в index. Затем вы можете прочитать одну запись, используя тело вашего цикла.

const Int32 RecordSize = (2 + 128)*sizeof(Int32) + sizeof(Boolean);
fileStream.Seek(RecordSize * index, SeekOrigin.Begin); 

Если у вас есть много разных записей фиксированного размера, и вы боитесь, что ручной ввод размера записи для каждой записи подвержен ошибкам, вы можете разработать схему, основанную на отражении и пользовательских атрибутах.

Создайте атрибут для определения размера массивов:

[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
sealed class ArraySizeAttribute : Attribute {

  public ArraySizeAttribute(Int32 length) {
    Length = length;
  }

  public Int32 Length { get; private set; }

}

Используйте атрибут в вашем типе записи:

private struct sTestStruct {   
  public int numberOne;   
  public int numberTwo;   
  [ArraySize(128)]
  public int[] numbers; // This is ALWAYS 128 ints long.   
  public bool trueFalse;   
}

Затем вы можете вычислить размер записи, используя этот пример кода:

Int32 GetRecordSize(Type recordType) {
  return recordType.GetFields().Select(fieldInfo => GetFieldSize(fieldInfo)).Sum();
}

Int32 GetFieldSize(FieldInfo fieldInfo) {
  if (fieldInfo.FieldType.IsArray) {
    // The size of an array is the size of the array elements multiplied by the
    // length of the array.
    var arraySizeAttribute = (ArraySizeAttribute) Attribute.GetCustomAttribute(fieldInfo, typeof(ArraySizeAttribute));
    if (arraySizeAttribute == null)
      throw new InvalidOperationException("Missing ArraySizeAttribute on array.");
    return GetTypeSize(fieldInfo.FieldType.GetElementType())*arraySizeAttribute.Length;
  }
  else
    return GetTypeSize(fieldInfo.FieldType);
}

Int32 GetTypeSize(Type type) {
  if (type == typeof(Int32))
    return 4;
  else if (type == typeof(Boolean))
    return 1;
  else
    throw new InvalidOperationException("Unexpected type.");
}

Используйте это так:

var recordSize = GetRecordSize(typeof(sTestStruct));
fileStream.Seek(recordSize * index, SeekOrigin.Begin); 

Возможно, вам придется немного расширить этот код, чтобы использовать его в работе.

1 голос
/ 16 августа 2010

Определите свою структуру следующим образом:

struct sTestStruct
{
    public int numberOne;
    public int numberTwo;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=128)]
    public int[] numbers; // This is ALWAYS 128 ints long. 
    public bool trueFalse;
}

И используйте Marshal.Sizeof(typeof(sTestStruct)).

1 голос
/ 16 августа 2010

Из всего, что я прочитал, способ, которым вы делаете это, является лучшим методом для чтения в двоичных данных, так как он имеет наименьшее количество ошибок, где все может пойти не так.

...