从运行时已知类型的二进制文件中读取值

时间:2014-11-06 05:00:24

标签: c# marshalling binaryfiles unsafe-pointers

我试图从二进制文件中读取一系列值,但直到运行时我才知道值类型是什么。

简化示例

我有一个10字节长的二进制文件。字节按顺序表示intfloatshort。我在编译时不知道这个,但我在运行时知道这个,有这样的数组:

        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信息以某种方式&#34;自动化&#34;此

我尝试了什么

我想到的一个想法是将原始字节读入数组,然后在字节数组上执行转换。所以,让我们说我的数组看起来像这样:

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

其中分别包含intfloatshort值123456,123.456和12345。现在我可以做到以下几点:

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

这似乎运作良好,但有两个问题:

  1. 我不知道如何将*ip封送回托管域名。
  2. 我仍然无法使用我的类型列表,如下所示:

        fixed (byte* bp = &buf[0])
        {
            (types[0])* ip = ((types[0])*)bp;      // both errors here
            Console.WriteLine("int ptr: {0}", *ip);
        }
    
  3. 这会在指示的行上产生两个编译时错误:

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

    到目前为止,我所想过的就是这一切。

    我希望有人可以提供帮助。我觉得我错过了一些简单的东西,这会让我的生活变得更轻松。

    更新

    我已经尝试过Peter Duniho的建议并且看起来效果很好,尽管与大if块相比,性能会有很小的提升。

    以下是~100 MB文件的一些结果(所有时间都以毫秒为单位):

    彼得的方法:

    2025
    2003
    1954
    1979
    1958
    

    if阻止:

    1531
    1488
    1486
    1489
    

    没有什么太重要的,虽然因为我计划使用更大,更大的文件(在GB范围内),这几百毫秒加起来,所以我会坚持使用丑陋的if块直到我找到一些快速的东西。

1 个答案:

答案 0 :(得分:1)

我不是百分百肯定我明白你实际上试图解决这个问题的哪一部分。但根据我的想法,我就是这样做的:

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