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

一度に 1 つの構造体しかユーザーに表示できないため、一度に複数のレコードを読み取る意味がないため、これはあまりしたくありません。したがって、次のようなものを使用して特定のレコードを読み取ることができると考えました。

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

しかし、sTestStruct のサイズがわからないため、配列のサイズを事前に定義することはできません。

4

3 に答える 3

2

sTestStruct1つの連続したメモリに保存されずsizeof(sTestStruct)、ファイル内のレコードのサイズに直接関係しません。numbersメンバーは、読み取りコードで自分自身を割り当てる配列への参照です。

ただし、レコードサイズは定数値であるため、コードで簡単に指定できます。このコードは、でレコードを検索しindexます。次に、ループの本体を使用して1つのレコードを読み取ることができます。

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 に答える