13

If I wanted to fill a structure from a binary file, I would use something like this:

using (BinaryReader br = new BinaryReader(File.Open(filename, FileMode.Open)))
{
    myStruct.ID = br.ReadSingle();
    myStruct.name = br.ReadBytes(20);
}

However, I must read the whole file into a byte array before deserializing, because I want to do some pre-processing. Is there any managed way to fill my structure from the byte array, preferably similar to the one above?

8
  • 4
    You should consider making your type serializable. If that is something you are interested in, i will provide a sample. See "BinaryFormatter" for Binary Serialization. Commented Jul 5, 2011 at 18:10
  • 1
    In my experience BinaryFormatter is rarely the correct choice for serializing data. Commented Jul 5, 2011 at 19:08
  • 1
    That depends very much on the situation. I usually need a well defined file format, so I use something like Linq-to-Json or Linq-to-Xml to transform between my in-memory representation and the file format. Sometimes protobuf is nice because it's very compact. And in some rare cases, if you don't need versioning and can live with its deeply invasive nature BinaryFormatter can be the right choice. A savegame in a game is one of the few cases that fits BinaryFormatter IMO. Commented Jul 5, 2011 at 19:24
  • 1
    Another big issue about the BinaryFormatter is that you must trust the file absolutely. Whoever created the file is most likely able to execute code in the context of your program. Commented Jul 5, 2011 at 19:47
  • 1
    Linq-To-Json/Xml technically isn't serialization. The transform between the representations is done manually, which is obviously much more work. But I think for stable file formats the clean separation of in memory representation and storage format is usually worth the additional work. Commented Jul 6, 2011 at 12:12

3 Answers 3

16

This is a sample to take some data (actually a System.Data.DataSet) and serialize to an array of bytes, while compressing using DeflateStream.

try
{
    var formatter = new BinaryFormatter();
    byte[] content;
    using (var ms = new MemoryStream())
    {
         using (var ds = new DeflateStream(ms, CompressionMode.Compress, true))
         {
             formatter.Serialize(ds, set);
         }
         ms.Position = 0;
         content = ms.GetBuffer();
         contentAsString = BytesToString(content);
     }
}
catch (Exception ex) { /* handle exception omitted */ }

Here is the code in reverse to deserialize:

        var set = new DataSet();
        try
        {
            var content = StringToBytes(s);
            var formatter = new BinaryFormatter();
            using (var ms = new MemoryStream(content))
            {
                using (var ds = new DeflateStream(ms, CompressionMode.Decompress, true))
                {
                    set = (DataSet)formatter.Deserialize(ds);                        
                }
            }
        }
        catch (Exception ex)
        {
            // removed error handling logic!
        }

Hope this helps. As Nate implied, we are using MemoryStream here.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks a lot, this should be more helpful with the big structures. Just a question - does changing the structure alignments affect the deserialization result?
I think the structure alignment affects serialization and deserialization, but I'm not certain.
@Angel for the record, I strongly advise against BinaryFormatter for any kind of storage. It has several nasty quirks, and I have honestly lost count of the number of times I've had to help people struggling with lost data etc. Other serializers exist that are (IMO, based on extensive work in serialization) more robust, just a convenient, and much more efficient for both CPU and bandwidth.
Hey Mark - I am using this sample in code that I am working on now, I was using XmlSerializer, but switched to BinaryFormatter. Should I go back? What are some of the other options?
@Glenn That depends on what you want to use the serializer for.
1

Take a look at the BitConverter class. That might do what you need.

1 Comment

Thanks for the answer, that class really does what I need. The only small inconvenience is that I need to track the position manually.
0

For very simple structs which aren't Serializable and contain only base types, this works. I use it for parsing files which have a known format. Error checking removed for clarity.

using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;

namespace FontUtil
{
    public static class Reader
    {
        public static T Read<T>(BinaryReader reader, bool fileIsLittleEndian = false)
        {
            Type type = typeof(T);
            int size = Marshal.SizeOf(type);
            byte[] buffer = new byte[size];
            reader.Read(buffer, 0, size);
            if (BitConverter.IsLittleEndian != fileIsLittleEndian)
            {
                FieldInfo[] fields = type.GetFields();
                foreach (FieldInfo field in fields)
                {
                    int offset = (int)Marshal.OffsetOf(type, field.Name);
                    int fieldSize = Marshal.SizeOf(field.FieldType);
                    for (int b = offset, t = fieldSize + b - 1; b < t; ++b, --t)
                    {
                        byte temp = buffer[t];
                        buffer[t] = buffer[b];
                        buffer[b] = temp;
                    }
                }
            }
            GCHandle h = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            T obj = (T)Marshal.PtrToStructure(h.AddrOfPinnedObject(), type);
            h.Free();
            return obj;
        }
    }
}

Structs need to be declared like this (and can't contain arrays, I think, haven't tried that out - the endian swap would probably get confused).

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct NameRecord
{
    public UInt16 uPlatformID;
    public UInt16 uEncodingID;
    public UInt16 uLanguageID;
    public UInt16 uNameID;
    public UInt16 uStringLength;
    public UInt16 uStringOffset; //from start of storage area
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.