0

バイナリ ファイルから一連の値を読み込もうとしていますが、実行時まで値の型がわかりません。

簡単な例

長さが 10 バイトのバイナリ ファイルがあります。バイトは、順に an int、 a float、および a を表しますshort。私はコンパイル時にこれを知りませんが、実行時には次のような配列でこれを知っています:

        Type[] types = new Type[3];
        types[0] = typeof(int);
        types[1] = typeof(float);
        types[2] = typeof(short);

質問

このリストを取得したので、この情報を使用してファイルから値をすばやく読み取る方法はありますか? 私が考えることができる唯一の方法は、大きなifブロックを使用することですが、それは本当に醜いです:

        for (int i = 0; i < types.Length; i++)
        {
            if (types[i] == typeof(int))
            {
                int val = binaryfile.ReadInt32();
                //... etc ...
            }
            else if (types[i] == typeof(float))
            {
                float val = binaryfile.ReadSingle();
                //... etc ...
            }
            else if //... etc...
        }

しかし、これは醜く面倒です。Type配列内の情報を使用して、types何らかの形でこれを「自動化」できるかどうか疑問に思っています。

私が試したこと

私が考えたアイデアの 1 つは、生のバイトを配列に読み込んでから、バイト配列で変換を実行することでした。それでは、私の配列が次のようになっているとしましょう:

        byte[] buf = new byte[10] {
            0x40, 0xE2, 0x01, 0x00,
            0x79, 0xE9, 0xF6, 0x42,
            0x39, 0x30 };

これにはint、、、floatおよびshort値 123456、123.456、および 12345 がそれぞれ含まれます。これで、次のことができます。

        fixed (byte* bp = &buf[0])
        {
            int* ip = (int*)bp;
            Console.WriteLine("int ptr: {0}", *ip);
        }

これはうまく機能しているように見えますが、次の 2 つの問題があります。

  1. *ipマネージド ドメインにマーシャリングする方法がわかりません。
  2. 次のように、タイプリストをまだ使用できません。

        fixed (byte* bp = &buf[0])
        {
            (types[0])* ip = ((types[0])*)bp;      // both errors here
            Console.WriteLine("int ptr: {0}", *ip);
        }
    

これにより、次の行で 2 つのコンパイル時エラーが発生します。

Error   1   Invalid expression term ')'
Error   2   ) expected

試してみようと思ったのはここまでです。

誰かが助けてくれることを願っています。人生をずっと楽にしてくれるシンプルなものが欠けているように感じます。

アップデート

ifPeter Duniho の提案を試してみましたが、大きなブロックに比べてパフォーマンスの低下はわずかですが、うまく機能しているようです。

〜 100 MB のファイルからのいくつかの結果を次に示します (すべての時間はミリ秒単位です)。

ピーターの方法:

2025
2003
1954
1979
1958

ifブロック:

1531
1488
1486
1489

ifそれほど重要なことはありませんが、私ははるかに大きなファイル (GB 範囲) で作業する予定なので、これらの数百ミリ秒が加算されるため、同じくらい高速なものが見つかるまで、醜いブロックを使い続けるつもりです。

4

1 に答える 1

1

この問題のどの部分を実際に解決しようとしているのかを 100% 理解しているとは言えません。しかし、あなたが求めていると思うことに基づいて、これは私がそれを行う方法です:

class Program
{
    static readonly Dictionary<Type, Func<byte[], int, Tuple<object, int>>> _converters =
        new Dictionary<Type, Func<byte[], int, Tuple<object, int>>>
        {
            { typeof(int), (rgb, ib) =>
                Tuple.Create((object)BitConverter.ToInt32(rgb, ib), sizeof(int)) },
            { typeof(float), (rgb, ib) =>
                Tuple.Create((object)BitConverter.ToSingle(rgb, ib), sizeof(float)) },
            { typeof(short), (rgb, ib) =>
                Tuple.Create((object)BitConverter.ToInt16(rgb, ib), sizeof(short)) },
        };

    static void Main(string[] args)
    {
        Type[] typeMap = { typeof(int), typeof(float), typeof(short) };
        byte[] inputBuffer =
            { 0x40, 0xE2, 0x01, 0x00, 0x79, 0xE9, 0xF6, 0x42, 0x39, 0x30 };
        int ib = 0, objectIndex = 0;

        while (ib < inputBuffer.Length)
        {
            Tuple<object, int> current =
                _converters[typeMap[objectIndex++]](inputBuffer, ib);
            Console.WriteLine("Value: " + current.Item1);
            ib += current.Item2;
        }
    }
}
于 2014-11-06T06:07:07.150 に答える