Take the tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I'm working on a communications layer for a system that reads data from a TCPIP client that is formatted as fixed-width ASCII (yeah, old school). I was quite surprised that there seemed to be no built in way to do this, and ended up using the following simple StreamReader subclass:

/// <summary>
/// A Stream reader that reads values as fixed width fields from a stream
/// </summary>
class FixedWidthFieldStreamReader : StreamReader
{
    #region Private/Protected fields
    private char[] buffer; // Local buffer used to copy data before conversion
    #endregion

    #region Methods
    /// <summary>
    /// Instantiates a new FixedWidthFieldStreamReader for a stream
    /// </summary>
    /// <param name="stream">Stream to read from</param>
    /// <param name="initialBufferSize">Initial size of the buffer used to copy data before formatting</param>
    /// <param name="encoding">Encoding to use when reading from the stream</param>
    public FixedWidthFieldStreamReader(Stream stream, int initialBufferSize, Encoding encoding)
        : base(stream, encoding)
    {
        buffer = new char[initialBufferSize];
    }

    /// <summary>
    /// Checks if the buffer exists and is large enough,
    /// and allocates or grows it if necessary.
    /// </summary>
    /// <param name="length">The required buffer length</param>
    private void EnsureBufferLength(int length)
    {
        if (null == buffer ||
            buffer.Length < length)
        {
            buffer = new char[length];
        }
    }

    /// <summary>
    /// Reads a number of bytes into the buffer
    /// </summary>
    /// <param name="length">The number of bytes to read</param>
    /// <returns>True if the required number of bytes was read, false otherwise</returns>
    private bool ReadToBuffer(int length)
    {
        EnsureBufferLength(length);

        // Read from the stream
        int read = Read(buffer, 0, length);
        return read == length;
    }

    /// <summary>
    /// Reads a specified number of bytes from the stream and 
    /// converts and returns the read value.
    /// </summary>
    /// <typeparam name="T">Type of the object to read and return</typeparam>
    /// <param name="length">Number of bytes in the field to read from the stream.</param>
    /// <returns>The read object if successful, or the default value for the type otherwise.</returns>
    public T Read<T>(int length) where T : IConvertible
    {
        if (ReadToBuffer(length))
        {
            return (T)Convert.ChangeType(new string(buffer, 0, length), typeof(T));
        }

        return default(T);
    }

    /// <summary>
    /// Skips a specified number of bytes in the stream
    /// </summary>
    /// <param name="length">The number of bytes to skip</param>
    public void Skip(int length)
    {
        // Ideally we should be able to just seek on the current stream, 
        //  but that seems to seek to an incorrect location?
        //this.BaseStream.Seek(length, SeekOrigin.Current);
        ReadToBuffer(length);
    }
    #endregion
}

This would be used something like this:

        using (MemoryStream stream = new MemoryStream(buffer))
        {
            stream.Seek((int)FieldOffsets.DATA, SeekOrigin.Begin);
            using (FixedWidthFieldStreamReader reader = new FixedWidthFieldStreamReader(stream, 15, Encoding.ASCII))
            {
                intVal = reader.Read<int>(3);
                stringVal = reader.Read<string>(15);
                floatVal= reader.Read<float>(5);
            }
        }

I have two questions based on this:

  1. Am I just missing some completely obvious existing utility to do this? It really does seem like a common problem that would have been solved by the framework team ages ago.
  2. Aside from the obvious optimization of having some type specific versions of Read that don't do the conversion, are there any suggestions to improve this approach?
share|improve this question
 
I considered posting there as well, but thought since the primary question was really "is there a better way to do this?" here would be more appropriate. –  FlintZA Jun 19 at 8:03
add comment

migrated from stackoverflow.com Jun 25 at 21:43

This question came from our site for professional and enthusiast programmers.

1 Answer

up vote 3 down vote accepted

I don't know if something does that already, but you could mimic StructLayout if you need to read multiple types of 'objects' or need reusability.

Basically just set starting point/offset and length for each property and read them.

public class Item
{
     // 3 chars starting from 0
    [Layout(0, 3)]
    public int Number { get; set; }

     // 15 chars starting from 3
    [Layout(3, 15)]
    public string Text { get; set; } 

    // 5 chars starting from 18
    [Layout(18, 5)]
    public float Number2 { get; set; } 
}

reader.Read<Item>()
share|improve this answer
 
+1 this is the way to go. In addition to explicit struct layout you could also look at fixed size buffers. –  MattDavey Jun 26 at 8:22
 
That is much more readable, good suggestion, thanks. –  FlintZA Jun 27 at 7:59
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.