简体   繁体   English

C# - 将字节数组转换为结构数组,反之亦然(反向)

[英]C# - Cast a byte array to an array of struct and vice-versa (reverse)

I would like to save a Color[] to a file.我想将 Color[] 保存到文件中。 To do so, I found that saving a byte array to a file using "System.IO.File.WriteAllBytes" should be very efficient.为此,我发现使用“System.IO.File.WriteAllBytes”将字节数组保存到文件应该非常有效。

I would like to cast my Color[] (array of struct) to a byte array into a safe way considering:我想以一种安全的方式将我的 Color[](结构数组)转换为字节数组:

  • Potential problem of little endian / big endian (I think it is impossible to happen but want to be sure)小端/大端的潜在问题(我认为不可能发生但想确定)
  • Having 2 differents pointer to the same memory which point to different type.有 2 个不同的指针指向指向不同类型的同一内存。 Does the garbage collection will know what to do - moving objects - deleting a pointer ???垃圾收集是否知道该怎么做 - 移动对象 - 删除指针???

If it is possible, it would be nice to have a generic way to cast array of byte to array of any struct (T struct) and vice-versa.如果可能,最好有一种通用的方法将字节数组转换为任何结构(T 结构)数组,反之亦然。

If not possible, why ?如果不可能,为什么?

Thanks, Eric谢谢,埃里克

I think that those 2 solutions make a copy that I would like to avoid and also they both uses Marshal.PtrToStructure which is specific to structure and not to array of structure:我认为这两个解决方案制作了一个我想避免的副本,而且它们都使用 Marshal.PtrToStructure 特定于结构而不是结构数组:

Since .NET Core 2.1, yes we can!从 .NET Core 2.1 开始,是的,我们可以! Enter MemoryMarshal .输入MemoryMarshal

We will treat our Color[] as a ReadOnlySpan<Color> .我们将Color[]视为ReadOnlySpan<Color> We reinterpret that as a ReadOnlySpan<byte> .我们将其重新解释为ReadOnlySpan<byte> Finally, since WriteAllBytes has no span-based overload, we use a FileStream to write the span to disk.最后,由于WriteAllBytes没有基于跨度的重载,我们使用FileStream将跨度写入磁盘。

var byteSpan = MemoryMarshal.AsBytes(colorArray.AsSpan());
fileStream.Write(byteSpan);

As an interesting side note, you can also experiment with the [StructLayout(LayoutKind.Explicit)] as an attribute on your fields.作为一个有趣的旁注,您还可以尝试将[StructLayout(LayoutKind.Explicit)]作为您的字段的属性。 It allows you to specify overlapping fields, effectively allowing the concept of a union.它允许您指定重叠字段,有效地允许联合的概念。

Here is a blog post on MSDN that illustrates this. 是 MSDN 上的一篇博客文章,说明了这一点。 It shows the following code:它显示了以下代码:

[StructLayout(LayoutKind.Explicit)]
public struct MyUnion
{
    [FieldOffset(0)]
    public UInt16 myInt;

    [FieldOffset(0)]
    public Byte byte1;

    [FieldOffset(1)]
    public Byte byte2;
}

In this example, the UInt16 field overlaps with the two Byte fields.在此示例中, UInt16字段与两个Byte字段重叠。

This seems to be strongly related to what you are trying to do.这似乎与您尝试做的事情密切相关。 It gets you very close, except for the part of writing all the bytes (especially of multiple Color objects) efficiently.除了高效写入所有字节(尤其是多个Color对象)的部分之外,它让您非常接近。 :) :)

Regarding Array Type Conversion关于数组类型转换

C# as a language intentionally makes the process of flattening objects or arrays into byte arrays difficult because this approach goes against the principals of .NET strong typing. C# 作为一种语言故意使将对象或数组扁平化为字节数组的过程变得困难,因为这种方法违背了 .NET 强类型的原则。 The conventional alternatives include several serialization tools which are generally seen a safer and more robust, or manual serialization coding such as BinaryWriter .传统的替代方案包括几种序列化工具,这些工具通常被认为是更安全、更健壮的,或者手动序列化编码,例如BinaryWriter

Having two variables of different types point to the same object in memory can only be performed if the types of the variables can be cast, implicitly or explicitly.只有当变量的类型可以隐式或显式转换时,才能让两个不同类型的变量指向内存中的同一个对象。 Casting from an array of one element type to another is no trivial task: it would have to convert the internal members that keep track of things such as array length, etc.从一种元素类型的数组转换为另一种元素类型并非易事:它必须转换跟踪数组长度等内容的内部成员。

A simple way to write and read Color[] to file一种将 Color[] 写入和读取到文件的简单方法

void WriteColorsToFile(string path, Color[] colors)
{
    BinaryWriter writer = new BinaryWriter(File.OpenWrite(path));

    writer.Write(colors.Length);

    foreach(Color color in colors)
    {
        writer.Write(color.ToArgb());
    }

    writer.Close();
}

Color[] ReadColorsFromFile(string path)
{
    BinaryReader reader = new BinaryReader(File.OpenRead(path));

    int length = reader.ReadInt32();

    Colors[] result = new Colors[length];

    for(int n=0; n<length; n++)
    {
        result[n] = Color.FromArgb(reader.ReadInt32());
    }

    reader.Close();
}

You could use pointers if you really want to copy each byte and not have a copy but the same object, similar to this:如果您真的想复制每个字节并且没有副本但具有相同的对象,则可以使用指针,类似于:

var structPtr = (byte*)&yourStruct;
var size = sizeof(YourType);
var memory = new byte[size];
fixed(byte* memoryPtr = memory)
{
    for(int i = 0; i < size; i++)
    {
        *(memoryPtr + i) = *structPtr++;
    }
}
File.WriteAllBytes(path, memory);

I just tested this and after adding the fixed block and some minor corrections it looks like it is working correctly.我刚刚对此进行了测试,在添加了fixed块和一些小的更正后,它看起来工作正常。

This is what I used to test it:这是我用来测试它的:

public static void Main(string[] args)
{
    var a = new s { i = 1, j = 2 };
    var sPtr = (byte*)&a;
    var size = sizeof(s);
    var mem = new byte[size];
    fixed (byte* memPtr = mem)
    {
        for (int i = 0; i < size; i++)
        {
            *(memPtr + i) = *sPtr++;
        }
    }
    File.WriteAllBytes("A:\\file.txt", mem);
}

struct s
{
    internal int i;

    internal int j;
}

The result is the following:结果如下:

例子

(I manually resolved the hex bytes in the second line, only the first line was produced by the program) (我手动解析了第二行的十六进制字节,只有第一行是程序产生的)

Working code for reference (take care, I did not need the alpha channel in my case):供参考的工作代码(注意,在我的情况下我不需要 alpha 通道):

// ************************************************************************
// If someday Microsoft make Color serializable ...
    //public static void SaveColors(Color[] colors, string path)
    //{
    //  BinaryFormatter bf = new BinaryFormatter();
    //  MemoryStream ms = new MemoryStream();
    //  bf.Serialize(ms, colors);
    //  byte[] bytes = ms.ToArray();
    //  File.WriteAllBytes(path, bytes);
    //}

// If someday Microsoft make Color serializable ...
    //public static Colors[] LoadColors(string path)
    //{
    //  Byte[] bytes = File.ReadAllBytes(path);
    //  BinaryFormatter bf = new BinaryFormatter();
    //  MemoryStream ms2 = new MemoryStream(bytes);
    //  return (Colors[])bf.Deserialize(ms2);
    //}

    // ******************************************************************
    public static void SaveColorsToFile(Color[] colors, string path)
    {
        var formatter = new BinaryFormatter();

        int count = colors.Length;

        using (var stream = File.OpenWrite(path))
        {
            formatter.Serialize(stream, count);

            for (int index = 0; index < count; index++)
            {
                formatter.Serialize(stream, colors[index].R);
                formatter.Serialize(stream, colors[index].G);
                formatter.Serialize(stream, colors[index].B);
            }
        }
    }

    // ******************************************************************
    public static Color[] LoadColorsFromFile(string path)
    {
        var formatter = new BinaryFormatter();

        Color[] colors;

        using (var stream = File.OpenRead(path))
        {
            int count = (int)formatter.Deserialize(stream);
            colors = new Color[count];

            for (int index = 0; index < count; index++)
            {
                byte r = (byte)formatter.Deserialize(stream);
                byte g = (byte)formatter.Deserialize(stream);
                byte b = (byte)formatter.Deserialize(stream);

                colors[index] = Color.FromRgb(r, g, b);
            }
        }

        return colors;
    }

    // ******************************************************************
    public struct MyX
    {
        public int IntValue;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.U1)]
        public byte[] Array;

        MyX(int i, int b)
        {
            IntValue = b;
            Array = new byte[3];
        }

        public MyX ToStruct(byte []ar)
        {

            byte[] data = ar;//= { 1, 0, 0, 0, 9, 8, 7 }; // IntValue = 1, Array = {9,8,7}
            IntPtr ptPoit = Marshal.AllocHGlobal(data.Length);
            Marshal.Copy(data, 0, ptPoit, data.Length);

            MyX x = (MyX)Marshal.PtrToStructure(ptPoit, typeof(MyX));
            Marshal.FreeHGlobal(ptPoit);

            return x;
        }
        public byte[] ToBytes()
        {
            Byte[] bytes = new Byte[Marshal.SizeOf(typeof(MyX))];
            GCHandle pinStructure = GCHandle.Alloc(this, GCHandleType.Pinned);
            try
            {
                Marshal.Copy(pinStructure.AddrOfPinnedObject(), bytes, 0, bytes.Length);
                return bytes;
            }
            finally
            {
                pinStructure.Free();
            }
        }
    }

    void function()
    {
        byte[] data = { 1, 0, 0, 0, 9, 8, 7 }; // IntValue = 1, Array = {9,8,7}
        IntPtr ptPoit = Marshal.AllocHGlobal(data.Length);
        Marshal.Copy(data, 0, ptPoit, data.Length);

        var x = (MyX)Marshal.PtrToStructure(ptPoit, typeof(MyX));
        Marshal.FreeHGlobal(ptPoit);

        var MYstruc = x.ToStruct(data);


        Console.WriteLine("x.IntValue = {0}", x.IntValue);
        Console.WriteLine("x.Array = ({0}, {1}, {2})", x.Array[0], x.Array[1], x.Array[2]);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在C#中将十进制数组转换为字节数组,反之亦然 - Converting decimal array to byte array and vice-versa in C# C#字节数组为字符串,反之亦然 - C# byte array to string, and vice-versa 在C#中将字符串数组转换为简单字符串,反之亦然 - converting an array of strings into a simple string and vice-versa in c# 依次将类变量序列化为C#中的字节数组,反之亦然 - Serialize class variables sequentially to byte array in C# vice versa 在C#中将字节数组转换为字符串,反之亦然 - Converting a byte array to string and vice versa in C# 在 struct 和 Span 之间进行无不安全的转换<byte>反之亦然。C# 和 NET 5</byte> - Cast without unsafe between struct and Span<byte> and vice versa in .C# and NET 5 BitmapImage到字节数组,反之亦然 - BitmapImage to byte array and vice versa C#有符号和无符号整数到Big Endian字节数组,反之亦然,使用具有“最佳”性能的Bitwise方式 - C# Signed & Unsigned Integral to Big Endian Byte Array, and vice versa using Bitwise way with “best” performance C#int64列表转换为字节数组,反之亦然? - C# int64 list to byte array and vice versa casting? 如何在C#中将位图转换为一维字节数组,反之亦然? - How can I convert a Bitmap to a 1D byte array and vice versa in C#?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM