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

本文关键字:二进制文件 读取 类型 运行时 | 更新日期: 2023-09-27 18:29:23

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

简化示例

我有一个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...
        }

但这是丑陋和繁琐的。我想知道是否可以使用types数组中的Type信息以某种方式"自动化"这一点。

我尝试过的

我想到的一个想法是将原始字节读入数组,然后对字节数组执行转换。假设我的数组是这样的:

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

这在所示的行上产生了两个编译时错误:

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块,直到我找到同样快的东西。

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

我不能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;
        }
    }
}