Чтение значений из двоичного файла, типы которого известны во время выполнения

Я пытаюсь прочитать серию значений из двоичного файла, но я не буду знать, какие типы значений, до времени выполнения.

Упрощенный пример

У меня есть двоичный файл длиной 10 байт. Байты представляют, по порядку, int, floatи 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 массив, чтобы как-то "автоматизировать" это.

Что я пробовал

Одна мысль, о которой я подумал, - это чтение необработанных байтов в массив, а затем выполнение преобразования в байтовом массиве. Итак, допустим, мой массив выглядит так:

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

Кажется, это хорошо работает, но есть две проблемы:

  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

Это все, что я хотел попробовать до сих пор.

Я надеюсь, что кто-то может помочь. Я чувствую, что упускаю что-то простое, что сделало бы мою жизнь намного проще.

Обновить

Я попробовал предложение Питера Дунихо, и оно, кажется, работает довольно хорошо, хотя производительность по сравнению с большим if блок.

Вот некоторые результаты из файла ~100 МБ (время указано в мс):

Метод Петра:

2025
2003
1954
1979
1958

if блок:

1531
1488
1486
1489

Ничего особенного, хотя, поскольку я планирую работать с гораздо более крупными файлами (в диапазоне ГБ), эти несколько сотен миллисекунд складываются, поэтому я буду придерживаться уродливого if блокировать, пока я не найду что-нибудь так быстро.

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;
        }
    }
}
Другие вопросы по тегам