A C# Equivalent of C's Fread File I/O

A C# equivalent of C's fread file i/o

There isn't anything wrong with using the P/Invoke marshaller, it is not unsafe and you don't have to use the unsafe keyword. Getting it wrong will just produce bad data. It can be a lot easier to use than explicitly writing the deserialization code, especially when the file contains strings. You can't use BinaryReader.ReadString(), it assumes that the string was written by BinaryWriter. Make sure however that you declare the structure of the data with a struct declaration, this.GetType() is not likely to work out well.

Here's a generic class that will make it work for any structure declaration:

  class StructureReader<T> where T : struct {
private byte[] mBuffer;
public StructureReader() {
mBuffer = new byte[Marshal.SizeOf(typeof(T))];
}
public T Read(System.IO.FileStream fs) {
int bytes = fs.Read(mBuffer, 0, mBuffer.Length);
if (bytes == 0) throw new InvalidOperationException("End-of-file reached");
if (bytes != mBuffer.Length) throw new ArgumentException("File contains bad data");
T retval;
GCHandle hdl = GCHandle.Alloc(mBuffer, GCHandleType.Pinned);
try {
retval = (T)Marshal.PtrToStructure(hdl.AddrOfPinnedObject(), typeof(T));
}
finally {
hdl.Free();
}
return retval;
}

A sample declaration for the structure of the data in the file:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct Sample {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)]
public string someString;
}

You'll need to tweak the structure declaration and the attributes to get a match with the data in the file. Sample code that reads a file:

  var data = new List<Sample>();
var reader = new StructureReader<Sample>();
using (var stream = new FileStream(@"c:\temp\test.bin", FileMode.Open, FileAccess.Read)) {
while(stream.Position < stream.Length) {
data.Add(reader.Read(stream));
}
}

What is the C# / .net equivalent of writing binary data directly to a struct?

I think you're asking about the StructLayoutAttribute and the FieldOffsetAttribute.

Example (snippet) from MSDN site:

[StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)]
public class MySystemTime
{
[FieldOffset(0)]public ushort wYear;
[FieldOffset(2)]public ushort wMonth;
[FieldOffset(4)]public ushort wDayOfWeek;
[FieldOffset(6)]public ushort wDay;
[FieldOffset(8)]public ushort wHour;
[FieldOffset(10)]public ushort wMinute;
[FieldOffset(12)]public ushort wSecond;
[FieldOffset(14)]public ushort wMilliseconds;
}

How to read byte blocks into struct

Assuming this is C#, I wouldn't create a struct as a FileEntry type. I would replace char[20] with strings and use a BinaryReader - http://msdn.microsoft.com/en-us/library/system.io.binaryreader.aspx to read individual fields. You must read the data in the same order as it was written.

Something like:

class FileEntry {
byte Value1;
char[] Filename;
byte Value2;
byte[] FileOffset;
float whatever;
}

using (var reader = new BinaryReader(File.OpenRead("path"))) {
var entry = new FileEntry {
Value1 = reader.ReadByte(),
Filename = reader.ReadChars(12) // would replace this with string
FileOffset = reader.ReadBytes(3),
whatever = reader.ReadFloat()
};
}

If you insist having a struct, you should make your struct immutable and create a constructor with arguments for each of your field.

 

Is it possible to cast a pinned byte array to a struct so that bidirectional changes are possible in C#?

You just cast the pointer (sometimes you need to go via void* in the middle):

struct Foo
{
public int Bar;
}
static unsafe void Main()
{
byte[] buffer = new byte[10];
fixed (byte* untyped = buffer)
{
var typed = (Foo*)untyped;
typed[0].Bar = 123;
}
// buffer has the changes
}

If you need to offset into the buffer, then use byte* untyped = &buffer[offset].

If you want a raw struct pointer, then:

fixed (byte* ptr = buffer)
{
var typed = (Foo*)ptr;
Foo* foo = &typed[0];
foo->Bar = 123;
}

However, note that you can't pass a Foo* to methods expecting a Foo or ref Foo.

Preferred way to parse a custom binary flat file?

The link Hans Passant provided has the answer. I would give him credit, but I'm not sure what to do since he posted as a comment instead of an answer.



Related Topics



Leave a reply



Submit