Как получить доступ к отдельным элементам в сериализованном массиве? - PullRequest
1 голос
/ 16 января 2020

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

Очевидно, что простой, но медленный способ заключается в использовании BitConverter.GetBytes(timestamp) для преобразовать каждую метку времени в байты и затем сохранить их в файле. Затем я могу получить доступ к каждому элементу по отдельности в файле и использовать свой собственный алгоритм двоичного поиска, чтобы найти метку времени, которая соответствует желаемой метке времени.

Однако я обнаружил, что BinaryFormatter невероятно эффективен (в несколько раз быстрее, чем protobuf- net и любой другой сериализатор, который я пробовал) в отношении сериализации / десериализации массивов типов значений. Поэтому я попытался сериализовать массив временных меток в двоичную форму. Однако, по-видимому, теперь это не позволит мне получить доступ к отдельным временным меткам в файле без предварительной десериализации всего массива.

Есть ли способ получить доступ к отдельным элементам в двоичном виде после сериализации всего массива элементов через BinaryFormatter?

Вот фрагмент кода, демонстрирующий, что я имею в виду:

var sampleArray = new int[5] { 1,2,3,4,5};

        var serializedSingleValueArray = sampleArray.SelectMany(x => BitConverter.GetBytes(x)).ToArray();
        var serializedArrayofSingleValues = Serializers.BinarySerializeToArray(sampleArray);

        var deserializesToCorrectValue = BitConverter.ToInt32(serializedSingleValueArray, 0); //value = 1 (ok)
        var wrongDeserialization = BitConverter.ToInt32(serializedArrayofSingleValues, 0); //value = 256 (???)

Здесь функция сериализации:

public static byte[]BinarySerializeToArray(object toSerialize)
    {
        using (var stream = new MemoryStream())
        {
            Formatter.Serialize(stream, toSerialize);
            return stream.ToArray();
        }
    }

Редактировать: мне не нужно заботиться об эффективном потреблении памяти или размерах файлов, поскольку в настоящее время они не являются узкими местами. Именно скорость сериализации и десериализации является для меня узким местом с большими гигабайтовыми большими двоичными файлами и, следовательно, с очень большими массивами примитивов.

Ответы [ 2 ]

1 голос
/ 17 января 2020

Если ваша проблема заключается просто в том, «как преобразовать массив структуры в байт []», у вас есть другие варианты, кроме BitConverter. BitConverte r для одиночных значений, класс Buffer для массивов.

        double[] d = new double[100];
        d[4] = 1235;
        d[8] = 5678;
        byte[] b = new byte[800];
        Buffer.BlockCopy(d, 0, b, 0, d.Length*sizeof(double));

        // just to test it works
        double[] d1 = new double[100];
        Buffer.BlockCopy(b, 0, d1, 0, d.Length * sizeof(double));

Это делает копирование на уровне байтов без преобразования чего-либо и без перебора элементов.

You может поместить этот байтовый массив непосредственно в ваш поток (не StreamWriter, не форматер) Мысленный метод, прочитает элемент, по какой-то причине сначала сохранит его, прежде чем он перейдет в файл.

Если это единственное, что вы пишете в свой файл - вам не нужно писать массив. длина в файле, вы можете использовать длину файла для этого.

Чтобы прочитать 100-е двойное значение в файле:

    file.Seek(100*sizeof(double), SeekOrigin.Begin);
    byte[] tmp = new byte[8];
    f.Read(tmp, 0, 8);
    double value = BitConverter.ToDouble(tmp, 0);

Здесь для одиночного значения можно использовать BitConverter.

. Это решение для. NET Framework, C# <= 7.0 </p>

Для. NET Стандарт / .NET Core, C# 8.0 у вас есть больше опций с Span<T>, который дает вам доступ к внутреннему память, без копирования данных.

0 голосов
/ 16 января 2020

Битконвертер не является «медленной» версией, это просто способ преобразовать все в последовательность байтов []. Это на самом деле не дорого, просто интерпретировать память по-разному.

Вычисление позиции в файле, загрузка 8 байтов, преобразование в DateTime, все готово.

Вы должны делать это только с простые структурированные файлы, и с простыми структурированными файлами вам не нужен двоичный форматер. Просто загрузите / сохраните один массив в один файл. Таким образом, вы можете быть уверены, что ваши позиции в файлах могут быть вычислены.

Другими словами. Сохраните свой массив, Date byte Date, чем вы можете загрузить его также Date by Date.

Запись с одним стилем обработки, чтение с другим, всегда плохая идея.

...