Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / file

BufferedFileReader & BufferedFileWriter: extending BinaryReader and BinaryWriter using BufferedStream

4.63/5 (7 votes)
8 Mar 2011CPOL 31.2K  
Simple helper classes to read/write files using BufferedStream plus a few handy methods (read/write structs or simple arrays)
I often need to read/write simple arrays or structs from files and I also wanted to take advantage of the BufferedStream class to access files more efficiently.
And I'm always tired of instanciating 2 or 3 different objects just to open/create a file using its name.

So I wrote simple wrappers that some of you will maybe find useful.

Here is an example of how to use the classes:
//opens a file for reading
BufferedFileReader reader = new BufferedFileReader(fileName);
//all methods from BinaryReader are available
int myInt = reader.ReadInt32();
...
//reads 100 floats
float[] myArray = reader.ReadFloatArray(100);
//reads a struct
YourStruct theStruct = reader.ReadStruct<yourstruct>();
reader.Close();

//creates a new file for writing
BufferedFileWriter writer = new BufferedFileWriter(fileName2);
//all methods from BinaryWriter are available
writer.Write(myInt);
...
//writes an int or float array
writer.Write(myArray);
//writes a struct
writer.WriteStruct(theStruct);
writer.Flush();
writer.Close();
</yourstruct>


Here are the classes:

BinaryReaderEx contains methods to read int arrays, float arrays, and structs (to be used with Marshaled structs), and also implements the Seek method which is not present in the current .NET Framework.

C#
/// <summary>
/// Adds a few features to BinaryReader.
/// </summary>
public class BinaryReaderEx : BinaryReader
{
    /// <summary>
    /// Initializes a new instance of the BinaryReaderEx class
    /// based on the supplied stream and using UTF8Encoding.
    /// </summary>
    /// <param name="input">A stream.</param>
    public BinaryReaderEx(Stream input)
        : base(input)
    {
    }
    /// <summary>
    /// Initializes a new instance of the BinaryReaderEx class
    /// based on the supplied stream and a specific character encoding.
    /// </summary>
    /// <param name="input">The supplied stream.</param>
    /// <param name="encoding">The character encoding.</param>
    public BinaryReaderEx(Stream input, Encoding encoding)
        : base(input, encoding)
    {
    }
    /// <summary>
    /// Initializes a new instance of the BinaryReaderEx class
    /// by creating a stream on the supplied file name.
    /// </summary>
    /// <param name="fileName">Name of the file to open.</param>
    /// <param name="fileMode">How to open the file.</param>
    public BinaryReaderEx(string fileName, FileMode fileMode)
        : base(File.Open(fileName, fileMode, FileAccess.Read, FileShare.ReadWrite))
    {
    }
    /// <summary>
    /// Initializes a new instance of the BinaryReaderEx class
    /// by creating a stream on the supplied existing file name.
    /// </summary>
    /// <param name="fileName">Name of the file to open.
    /// The file must exist.</param>
    public BinaryReaderEx(string fileName)
        : this(fileName, FileMode.Open)
    {
    }

    /// <summary>
    /// Reads a structure from the file.
    /// </summary>
    /// <typeparam name="T">Type of the structure.</typeparam>
    /// <returns>The structure read from the file.</returns>
    public T ReadStruct<T>()
    {
        Type type = typeof(T);
        int size = Marshal.SizeOf(type);
        byte[] bytes = ReadBytes(size);
        GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
        T theStruct = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), type);
        handle.Free();
        return theStruct;
    }

    /// <summary>
    /// Sets the position within the current stream.
    /// The Seek method in not implemented in the BinaryReader class (strange?).
    /// </summary>
    /// <param name="offset">A byte offset relative to origin.</param>
    /// <param name="origin">A value indicating the reference point
    /// from which to obtain the new position.</param>
    /// <returns>The new position within the current stream.</returns>
    public long Seek(long offset, SeekOrigin origin)
    {
        return BaseStream.Seek(offset, origin);
    }

    /// <summary>
    /// Reads an int array from the file.
    /// </summary>
    /// <param name="length">Number of elements to read.</param>
    /// <returns>The array read from the file.</returns>
    public int[] ReadIntArray(int length)
    {
        int[] array = new int[length];
        byte[] bytes = ReadBytes(length * 4);
        int offset = 0;
        for (int i = 0; i < length; i++)
        {
            array[i] = BitConverter.ToInt32(bytes, offset);
            offset += 4;
        }
        return array;
    }
    /// <summary>
    /// Reads a float array from the file.
    /// </summary>
    /// <param name="length">Number of elements to read.</param>
    /// <returns>The array read from the file.</returns>
    public float[] ReadFloatArray(int length)
    {
        float[] array = new float[length];
        byte[] bytes = ReadBytes(length * 4);
        int offset = 0;
        for (int i = 0; i < length; i++)
        {
            array[i] = BitConverter.ToSingle(bytes, offset);
            offset += 4;
        }
        return array;
    }
}


BinaryWriterEx contains write methods for int and float arrays, structs (to be used with Marshaled structs), and the Seek method as well (the current framework already contains a Seek method, but the offset argument is an int instead of a long...).

C#
/// <summary>
/// Adds a few features to BinaryWriter.
/// </summary>
public class BinaryWriterEx : BinaryWriter
{
    /// <summary>
    /// Initializes a new instance of the BinaryWriterEx class
    /// based on the supplied stream and using UTF-8 as the encoding for strings.
    /// </summary>
    /// <param name="output">The output stream.</param>
    public BinaryWriterEx(Stream output)
        : base(output)
    {
    }
    /// <summary>
    /// Initializes a new instance of the BinaryWriterEx class
    /// based on the supplied stream and a specific character encoding.
    /// </summary>
    /// <param name="output">The supplied stream.</param>
    /// <param name="encoding">The character encoding.</param>
    public BinaryWriterEx(Stream output, Encoding encoding)
        : base(output, encoding)
    {
    }
    /// <summary>
    /// Initializes a new instance of the BinaryWriterEx class
    /// by creating a stream on the supplied file name.
    /// </summary>
    /// <param name="fileName">Name of the file used to create the stream.</param>
    /// <param name="fileMode">How to create the file.</param>
    public BinaryWriterEx(string fileName, FileMode fileMode)
        : this(File.Open(fileName, fileMode, FileAccess.Write, FileShare.ReadWrite))
    {
    }
    /// <summary>
    /// Initializes a new instance of the BinaryWriterEx class
    /// by creating a stream on the supplied file name.
    /// </summary>
    /// <param name="fileName">Name of the file to create.
    /// If the file already exists, its content will be destroyed.</param>
    public BinaryWriterEx(string fileName)
        : this(fileName, FileMode.Create)
    {
    }

    /// <summary>
    /// Sets the position within the current stream.
    /// The BinaryWriter.Seek method taking a long offset is not implemented (strange?).
    /// </summary>
    /// <param name="offset">A byte offset relative to origin.</param>
    /// <param name="origin">A value indicating the reference point
    /// from which to obtain the new position.</param>
    /// <returns>The new position within the current stream.</returns>
    public long Seek(long offset, SeekOrigin origin)
    {
        Flush();
        return BaseStream.Seek(offset, origin);
    }
    /// <summary>
    /// Writes a structure to the file.
    /// </summary>
    /// <param name="theStruct">The structure to write.</param>
    public void WriteStruct(object theStruct)
    {
        byte[] bytes = new byte[Marshal.SizeOf(theStruct.GetType())];
        GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
        Marshal.StructureToPtr(theStruct, handle.AddrOfPinnedObject(), false);
        handle.Free();
        Write(bytes);
    }
    /// <summary>
    /// Writes an int array to the file.
    /// </summary>
    /// <param name="array">The array to write.</param>
    public void Write(int[] array)
    {
        byte[] bytes = new byte[4 * array.Length];
        int offset = 0;
        foreach (int value in array)
        {
            byte[] intBytes = BitConverter.GetBytes(value);
            bytes[offset++] = intBytes[0];
            bytes[offset++] = intBytes[1];
            bytes[offset++] = intBytes[2];
            bytes[offset++] = intBytes[3];
        }
        Write(bytes);
    }
    /// <summary>
    /// Writes a float array to the file.
    /// </summary>
    /// <param name="array">The array to write.</param>
    public void Write(float[] array)
    {
        byte[] bytes = new byte[4 * array.Length];
        int offset = 0;
        foreach (float value in array)
        {
            byte[] floatBytes = BitConverter.GetBytes(value);
            bytes[offset++] = floatBytes[0];
            bytes[offset++] = floatBytes[1];
            bytes[offset++] = floatBytes[2];
            bytes[offset++] = floatBytes[3];
        }
        Write(bytes);
    }
}


BufferedFileReader inherits from BinaryReaderEx and uses a BufferedStream to cache data.

C#
/// <summary>
/// Adds a buffering layer to the BinaryReaderEx class.
/// </summary>
public class BufferedFileReader : BinaryReaderEx
{
    /// <summary>
    /// The file name.
    /// </summary>
    public string FileName { get; private set; }

    /// <summary>
    /// Opens an existing file.
    /// </summary>
    /// <param name="fileName">The file to open.
    /// The file must exist.</param>
    /// <param name="size">The buffer size.</param>
    public BufferedFileReader(string fileName, int size)
        : base(new BufferedStream(new FileStream(fileName, FileMode.Open), size))
    {
        FileName = fileName;
    }
    /// <summary>
    /// Opens an existing file with a 64KB buffer size.
    /// </summary>
    /// <param name="fileName">The file to open.
    /// The file must exist.</param>
    public BufferedFileReader(string fileName)
        : this(fileName, 64 * 1024)
    {
    }
}


BufferedFileWriter inherits from BinaryWriterEx and uses a BufferedStream to cache data.
C#
/// <summary>
/// Adds a buffering layer to the BinaryWriterEx class.
/// </summary>
public class BufferedFileWriter : BinaryWriterEx
{
    /// <summary>
    /// The file name.
    /// </summary>
    public string FileName { get; private set; }

    /// <summary>
    /// Creates a new file.
    /// </summary>
    /// <param name="fileName">The file to create.
    /// If the file already exists, its content is destroyed.</param>
    /// <param name="size">The buffer size.</param>
    public BufferedFileWriter(string fileName, int size)
        : base(new BufferedStream(new FileStream(fileName, FileMode.Create), size))
    {
        FileName = fileName;
    }
    /// <summary>
    /// Creates a new file with a 64KB buffer size.
    /// </summary>
    /// <param name="fileName">The file to create.
    /// If the file already exists, its content is destroyed.</param>
    public BufferedFileWriter(string fileName)
        : this(fileName, 64 * 1024)
    {
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)