2013-07-24 126 views
2

我想將一個Color []保存到一個文件中。爲此,我發現使用「System.IO.File.WriteAllBytes」將字節數組保存到文件應該非常高效。C# - 將一個字節數組轉換爲一個結構數組,反之亦然(反轉)

我想投我的顏色[](結構數組)字節數組到一個安全的方式考慮:

  • 小端/大端(我認爲這是不可能發生的潛在問題但要確定)
  • 有2個不同指針指向指向不同類型的相同內存。垃圾回收會知道該怎麼做 - 移動對象 - 刪除一個指針?

如果可能的話,這將是很好有一個通用的方法來流延字節的數組的任何結構(T結構)和反之亦然的陣列。

如果不行,爲什麼?

感謝, 埃裏克

我認爲,這些解決方案2使副本,我想避免,也它們都採用Marshal.PtrToStructure這是特定的構建,而不是結構的數組:

+0

你想使用'byte []'的唯一原因是能夠調用'WriteAllBytes'嗎?還有其他「更安全」的技術,例如使用BinaryWriter或BinaryFormatter。 – nicholas

+0

今天我遇到了一篇文章,描述了一種做本來想做的事情的方法,但是它涉及到一個混合語言項目:混合使用C#和C++/CLI:http://www.codeproject.com/Articles/33713/ Generic-BinaryReader-and-BinaryWriter-Extensions – nicholas

+0

謝謝。我快速看了一下,但是我有點擔心。它似乎做了我想避免的副本。另外,要像枚舉一樣使用枚舉,我們可以使用屬性「標誌」。通過使用C#流,我認爲我們有相同的結果,而不必混合2種不同的語言。但是非常感謝,如果我還有其他字節轉換問題,我會牢記這一點,並會回到這裏。 –

回答

2

關於陣列類型轉換

作爲一種語言,C#有意使得將對象或數組變爲字節數組變得困難,因爲這種方法違背了.NET強類型的原則。傳統的替代方案包括幾種通常被認爲更安全和更強大的序列化工具,或者手動序列化編碼,例如BinaryWriter

只有當變量的類型可以隱式或隱式地轉換時,才能執行不同類型的兩個變量指向內存中的同一對象。從一個元素類型轉換到另一個元素不是一件簡單的任務:它必須轉換內部成員來跟蹤諸如數組長度等事物。

一個簡單的方法來寫和讀的顏色[]到文件

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(); 
} 
+0

謝謝。我已經有幾乎精確的解決方案,我將添加參考。你的一些小錯誤。微軟並沒有將Color標記爲可序列化,這是無聊的,因爲我本來可以使用少一點,也許更有效的代碼。但是,謝謝你重申我在賽道上並沒有錯。 –

1

你可以使用指針如果你真的想複製的每個字節,而不是有一個副本,但相同的對象,與此類似:

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

我只是測試這一點,並添加fixed塊,它看起來是正常工作的一些小的修改後。

這是我用來測試它:

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

結果如下:

example

(I手工解決了十六進制字節在第二行中,只有第一線由程序生成)

+0

我認爲你理解正確,但如果GC決定將對象移動到別的地方會發生什麼。我需要有一個安全的句柄來反對,我認爲在你的例子中情況並非如此。另外,我想演員爲了避免任何副本。一個結構數組應該是內存中的連續字節,我認爲可以使用一些不安全的代碼以安全的方式直接訪問它。 –

+0

@EricOuellet你可以用'fixed'塊來做到這一點。如果你確實想確定GC沒有改變任何東西,你可以使用'GC.SuppressFinalize(yourStruct)'來阻止它收集你的對象。我認爲它應該像這樣工作,因爲我在PC上獲得了正確的結果。 – pascalhein

+0

也許,但你做了一個副本,我想避免使用「投」 –

0

工作的代碼以供參考(照顧,我不需要在我的情況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; 
    } 

    // ****************************************************************** 
0
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]); 
    } 
相關問題