在C#中读/写结构化二进制数据的最佳方法是什么?

时间:2012-12-20 00:30:57

标签: c#

与C类似,我们可以使用结构指针来读取或编写结构化二进制数据,如文件头等,在C#中有类似的方法吗?

3 个答案:

答案 0 :(得分:8)

BinaryReader上使用BinaryWriterMemoryStream往往是我看来最好的方法。

解析二进制数据:

byte[] buf = f                     // some data from somewhere
using (var ms = new MemoryStream(buf, false)) {   // Read-only
    var br = new BinaryReader(ms);

    UInt32 len = br.ReadUInt32();
    // ...
}

生成二进制数据:

byte[] result;
using (var ms = new MemoryStream()) {   // Expandable
    var bw = new BinaryWriter(ms);

    UInt32 len = 0x1337;
    bw.Write(len);
    // ...

    result = ms.GetBuffer();   // Get the underlying byte array you've created.
}

它们允许您读取和写入大多数文件头等所需的所有基本类型,例如(U)Int16, 32, 64SingleDouble以及{ {1}},byte以及其中的数组。直接支持char,但仅限于

  

该字符串以长度为前缀,一次编码为整数7位。

如果您以这种方式从string以这种方式编写字符串,这对我来说似乎很有用。但这很容易,比如说你的字符串以DWord长度为前缀,后跟那么多的ASCII字符:

BinaryWriter

请注意,我int len = (int)br.ReadUInt32(); string s = Encoding.ASCII.GetString(br.ReadBytes(len)); BinaryReader个对象包含在BinaryWriter块中。这是因为,尽管它们是using(),但它们IDisposable所做的只是在基础流上调用Dispose()(在这些示例中为Dispose())。

由于所有MemoryStream / BinaryReader都是基础流周围的一组BinaryWriter / Read()包装器,我不明白为什么它们是{{ 1}}无论如何。当您尝试做正确的事情并在所有Write()上拨打IDisposable时,这会让您感到困惑,并且您的信息流突然被丢弃。

答案 1 :(得分:2)

要从二进制文件中读取任意结构的数据(struct),首先需要这样:

public static T ToStructure<T>(byte[] data)
{
    unsafe
    {
        fixed (byte* p = &data[0])
        {
            return (T)Marshal.PtrToStructure(new IntPtr(p), typeof(T));
        }
    };
}

然后你可以:

public static T Read<T>(BinaryReader reader) where T: new()
{
    T instance = new T();
    return ToStructure<T>(reader.ReadBytes(Marshal.SizeOf(instance)));
}

要编写,请将struct对象转换为字节数组:

public static byte[] ToByteArray(object obj)
{
    int len = Marshal.SizeOf(obj);
    byte[] arr = new byte[len];
    IntPtr ptr = Marshal.AllocHGlobal(len);
    Marshal.StructureToPtr(obj, ptr, true);
    Marshal.Copy(ptr, arr, 0, len);
    Marshal.FreeHGlobal(ptr);
    return arr;
}

...然后使用BinaryWriter将结果字节数组写入文件。

答案 2 :(得分:0)

这是一个简单的例子,展示了如何以二进制格式在文件中读取和写入数据。

using System;
using System.IO;
namespace myFileRead
{
    class Program
     {
       static void Main(string[] args)
       {
           // Let's create new data file.
           string myFileName = @"C:\Integers.dat";
            //check if already exists
            if (File.Exists(myFileName))
               {
                Console.WriteLine(myFileName + " already exists in the selected directory.");

                return;
            }

            FileStream fs = new FileStream(myFileName, FileMode.CreateNew);

            // Instantialte a Binary writer to write data

            BinaryWriter bw = new BinaryWriter(fs);

            // write some data with bw

            for (int i = 0; i < 100; i++)

            {
                    bw.Write((int)i);
            }

            bw.Close();
            fs.Close();

            // Instantiate a reader to read content from file
            fs = new FileStream(myFileName, FileMode.Open, FileAccess.Read);

            BinaryReader br = new BinaryReader(fs);

            // Read data from the file

            for (int i = 0; i < 100; i++)
            {
                //read data as Int32 
                Console.WriteLine(br.ReadInt32());
            }
            //close the file 
            br.Close();
            fs.Close();           
       }

    }

}