1

好的,所以我目前有一个包含未知数量的结构的二进制文件,如下所示:

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

到目前为止,我使用以下内容将所有结构读入 List<>:

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 的大小,结构不会让我预定义数组大小,所以我该怎么做呢?

4

3 回答 3

2

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); 

您可能需要稍微扩展此代码才能在生产中使用它。

于 2010-08-16T13:55:53.293 回答
1

从我读过的所有内容来看,你这样做的方式是读取二进制数据的最佳方法,因为它具有最少的可能出错的陷阱。

于 2010-08-16T13:59:52.707 回答
1

像这样定义你的结构:

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)).

于 2010-08-16T15:06:03.950 回答