Лучший способ прочитать короткий массив с диска на C #?

Мне нужно записывать массивы short [] размером 4 ГБ на диск и с диска, поэтому я нашел функцию для записи массивов и изо всех сил пытаюсь написать код для чтения массива с диска. Обычно я кодирую на других языках, поэтому, пожалуйста, простите меня, если моя попытка пока что немного жалкая:

using UnityEngine;
using System.Collections;
using System.IO;

public class RWShort : MonoBehaviour {

    public static void WriteShortArray(short[] values, string path)
    {
        using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))
        {
            using (BinaryWriter bw = new BinaryWriter(fs))
            {
                foreach (short value in values)
                {
                    bw.Write(value);
                }
            }
        }
    } //Above is fine, here is where I am confused: 


    public static short[] ReadShortArray(string path) 
    {
        byte[]  thisByteArray= File.ReadAllBytes(fileName);
        short[] thisShortArray= new short[thisByteArray.length/2];      
                for (int i = 0; i < 10; i+=2)
                {
                    thisShortArray[i]= ? convert from byte array;
                }


        return thisShortArray;
    }   
}

person DeltaEnfieldWaid    schedule 05.02.2020    source источник
comment
Вероятно, вы могли бы просто прочитать все байты и преобразовать их в короткие но 4гиг данных - это много! может быть проблема с памятью.   -  person the_lotus    schedule 05.02.2020
comment
Я никогда раньше не видел такого объявления переменных в C #: thisShort : short[] = new short[];   -  person Sam Axe    schedule 05.02.2020
comment
Привет, извини, что исправил. Это некоторые данные аудиоанализа, вычисление которых занимает 20-30 минут, поэтому, если я могу сохранить их на диск, я могу сэкономить это время, чтобы изучить их. это 50 * 44100 * 600 коротких значений.   -  person DeltaEnfieldWaid    schedule 05.02.2020
comment
Второй код не будет компилироваться, так как ничего не возвращается и ничего не сохраняется из Read. Код пытается разделить ввод на две равные части, но если не кратное 4, у вас будет левая половина и правая половина с нечетным количеством байтов, и чтение последнего короткого (int16) также не будет работать.   -  person jdweng    schedule 05.02.2020
comment
Думаю, вместо того, чтобы полагаться на длину массива, я бы использовал while (fs.Position < fs.Length). Я бы также переключился на LinkedList<short>, чтобы мне не пришлось выделять 4 ГБ непрерывной памяти. LinkedList сохраняет указатель на следующий элемент / элемент, поэтому выделение памяти не обязательно должно быть непрерывным.   -  person Sam Axe    schedule 05.02.2020
comment
опция readallbytes звучит очень разумно, если я могу прочитать 4 ГБ за 1-2 минуты обработки.   -  person DeltaEnfieldWaid    schedule 05.02.2020
comment
связанные: stackoverflow.com/q/3206391/103167   -  person Ben Voigt    schedule 05.02.2020


Ответы (2)


Шорты - это два байта, поэтому вам нужно читать каждый раз по два байта. Я также рекомендую использовать такой yield return, чтобы вы не пытались вытащить все в память за один раз. Хотя, если тебе нужны все шорты вместе, это тебе не поможет ... я думаю, это зависит от того, что ты с ними делаешь.

void Main()
{
    short[] values = new short[] {
        1, 999, 200, short.MinValue, short.MaxValue
    };

    WriteShortArray(values, @"C:\temp\shorts.txt");

    foreach (var shortInfile in ReadShortArray(@"C:\temp\shorts.txt"))
    {
        Console.WriteLine(shortInfile);
    }
}

public static void WriteShortArray(short[] values, string path)
{
    using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))
    {
        using (BinaryWriter bw = new BinaryWriter(fs))
        {
            foreach (short value in values)
            {
                bw.Write(value);
            }
        }
    }
}

public static IEnumerable<short> ReadShortArray(string path)
{
    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
    using (BinaryReader br = new BinaryReader(fs))
    {
        byte[] buffer = new byte[2];
        while (br.Read(buffer, 0, 2) > 0)
            yield return (short)(buffer[0]|(buffer[1]<<8)); 
    }
}

Вы также можете определить это так, воспользовавшись BinaryReader:

public static IEnumerable<short> ReadShortArray(string path)
{
    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
    using (BinaryReader br = new BinaryReader(fs))
    {
        while (br.BaseStream.Position < br.BaseStream.Length)
            yield return br.ReadInt16();
    }
}
person Michael Jones    schedule 05.02.2020
comment
привет спасибо! Я действительно очень благодарен. Я вижу, что смогу его реализовать, я был бы очень удивлен, если бы не смог запустить его сейчас. Это для сложной идентификации музыкальных инструментов, я пишу какие-то лабораторные эксперименты, математика проще, чем преобразование типов данных и управление памятью! - person DeltaEnfieldWaid; 05.02.2020

Сопоставление памяти с файлом - ваш друг, есть функция MemoryMappedViewAccessor.ReadInt16, которая позволит вам напрямую считывать данные с типом short из дискового кеша ОС. Также перегрузка Write(), которая принимает Int16. Также функции ReadArray и WriteArray, если вы вызываете функции, которым нужен традиционный массив .NET.

Обзор использования файлов с отображением в память в .NET в MSDN

Если вы хотите сделать это с помощью обычного файлового ввода-вывода, используйте размер блока 1 или 2 мегабайта и функцию Buffer.BlockCopy для массового перемещения данных между byte[] и short[], а также используйте функции FileStream, которые принимают byte[]. Забудьте о BinaryWriter или BinaryReader, забудьте о двух байтах за раз.

Также можно выполнять ввод-вывод непосредственно в массиве .NET с помощью p / invoke, см. мой ответ, используя ReadFile и передав свойство SafeFileHandle объекта FileStream здесь Но даже несмотря на то, что у него нет дополнительных копий, оно все равно не должно успевать за отображаемыми в память вызовами ReadArray и WriteArray.

person Ben Voigt    schedule 05.02.2020