将sbyte[,]存储到磁盘并返回的最佳方式是什么

本文关键字:返回 最佳 是什么 方式 磁盘 sbyte 存储 | 更新日期: 2023-09-27 18:30:09

我想用尽可能小的空间在磁盘上存储一个sbyte[,](保存或加载时间不能超过几秒钟),然后再取回。

我无法将其序列化为xml:无法序列化System.SByte[,]类型的对象。不支持多维数组

我无法将其转换为MemoryStream:无法从'sbyte[]'转换为'int'

除了创建一个文本文件并逐段循环外。。有什么选择。

如果它有任何不同,那么阵列的大小可以超过100000 x 100000。该文件还需要可由不同的操作系统和计算机使用。


更新

我将数组扁平化为1D sbyte[],然后将sbyte[]转换为流,并将其与包含维度的单独文件一起保存到磁盘。

Stream stream = new MemoryStream(byteArray);

将其用作将流保存到磁盘的基础。https://stackoverflow.com/a/5515894/937131

这是我写的一个测试用例,如果其他人觉得它有用的话,它会变得平坦和不平坦。

[TestMethod]
public void sbyteTo1dThenBack()
{
    sbyte[,] start = new sbyte[,]
    {
        {1, 2},
        {3, 4},
        {5, 6},
        {7, 8},
        {9, 10}
    };
    sbyte[] flattened = new sbyte[start.Length];
    System.Buffer.BlockCopy(start, 0, flattened, 0, start.Length * sizeof(sbyte));
    sbyte[,] andBackAgain = new sbyte[5, 2];
    Buffer.BlockCopy(flattened, 0, andBackAgain, 0, flattened.Length * sizeof(sbyte));
    var equal =
        start.Rank == andBackAgain.Rank &&
        Enumerable.Range(0, start.Rank).All(dimension => start.GetLength(dimension) == andBackAgain.GetLength(dimension)) &&
        andBackAgain.Cast<sbyte>().SequenceEqual(andBackAgain.Cast<sbyte>());
    Assert.IsTrue(equal);
}

将sbyte[,]存储到磁盘并返回的最佳方式是什么

根据我的评论,我觉得写出所有内容的字节数组等价物是实现这一目标的方法。这可能不是最有效的方法,并且缺少您需要提供的大量错误处理代码,但是,它在我的测试中有效。

编辑:此外,BitConverter.ToInt32()可能取决于处理器的"Endianness"。如果您打算在ARM或其他非x86系统上使用此代码,请参阅Scott Chamberlain关于如何修复此问题的评论。

public static class ArraySerializer
{
    public static void SaveToDisk(string path, SByte[,] input)
    {
        var length = input.GetLength(1);
        var height = input.GetLength(0);
        using (var fileStream = File.OpenWrite(path))
        {
            fileStream.Write(BitConverter.GetBytes(length), 0, 4);//Store the length
            fileStream.Write(BitConverter.GetBytes(height), 0, 4);//Store the height
            var lineBuffer = new byte[length];
            for (int h = 0; h < height; h++) 
            {
                for (int l = 0; l < length; l++) 
                {
                    unchecked //Preserve sign bit
                    {
                        lineBuffer[l] = (byte)input[h,l];
                    }
                }
                fileStream.Write(lineBuffer,0,length);
            }
        }
    }
    public static SByte[,] ReadFromDisk(string path)
    {
        using (var fileStream = File.OpenRead(path))
        {
            int length;
            int height;
            var intBuffer = new byte[4];
            fileStream.Read(intBuffer, 0, 4);
            length = BitConverter.ToInt32(intBuffer, 0);
            fileStream.Read(intBuffer, 0, 4);
            height = BitConverter.ToInt32(intBuffer, 0);
            var output = new SByte[height, length]; //Note, for large allocations, this can fail... Would fail regardless of how you read it back
            var lineBuffer = new byte[length];
            for (int h = 0; h < height; h++)
            {
                fileStream.Read(lineBuffer, 0, length);
                for (int l = 0; l < length; l++)
                    unchecked //Preserve sign bit
                    {
                        output[h,l] = (SByte)lineBuffer[l];
                    }
            }
            return output;
        }
    }
}

以下是我的测试方法:

void Main()
{
    var test = new SByte[20000, 25000];
    var length = test.GetLength(1);
    var height = test.GetLength(0);
    var lineBuffer = new byte[length];
    var random = new Random();
    //Populate with random data
    for (int h = 0; h < height; h++) 
    {
        random.NextBytes(lineBuffer);
        for (int l = 0; l < length; l++)
        {
            unchecked //Let's use first bit as a sign bit for SByte
            {
                test[h,l] = (SByte)lineBuffer[l];
            }
        }
    }
    var sw = Stopwatch.StartNew();
    ArraySerializer.SaveToDisk(@"c:'users'ed'desktop'test.bin", test);
    Console.WriteLine(sw.Elapsed);
    sw.Restart();
    var test2 = ArraySerializer.ReadFromDisk(@"c:'users'ed'desktop'test.bin");
    Console.WriteLine(sw.Elapsed);
    Console.WriteLine(test.GetLength(0) == test2.GetLength(0));
    Console.WriteLine(test.GetLength(1) == test2.GetLength(1));
    Console.WriteLine(Enumerable.Cast<SByte>(test).SequenceEqual(Enumerable.Cast<SByte>(test2))); //Dirty hack to compare contents... takes a very long time
}

在我的系统(带SSD)上,该测试需要大约2.7秒才能写入或读取20kx25k阵列的内容。要添加压缩,只需将FileStream封装在GZipStream中即可。