Monday, 22 August 2011

Hexidecimal logging

I do a fair bit of serial comms in my current job (interfacing to EMV terminals, printers etc), and needed to log a low-level trace of the binary data transferred to the com port.
I wanted the output to look something like the hex-editor of Norton Commander (now I'm showing my age). i.e. the part in blue below:
22/08/2011 10:34:54:293 Sending 'DisplayMessageRequest' request (33 bytes):
01 00 1D D2 01 00 01 18 09 00 09 0D 09 50 6C 65 ...?.........Ple 61 73 65 20 53 69 67 6E 20 52 65 63 65 69 70 74 ase Sign Receipt 93 ?
And here's a way to to this:


/// <summary>
/// Format a byte array for display in log
/// </summary>
/// <remarks>
/// Returns a string representation of the supplied byte-array, by showing hexidecimal 
/// values and associated character-equivalent, split up into lines of 16 bytes each
/// </remarks>
public static string FormatBinaryData(byte[] dataRead)
{
    int i = 0;
    StringBuilder sb = new StringBuilder();
    while (i < dataRead.Length)
    {
        sb.Append(dataRead[i].ToString("X2"));
        sb.Append(" ");
        i++;
        if (i % 16 == 0)
        {
            byte[] stringEquivBytes = new byte[16];
            Array.Copy(dataRead, i - 16, stringEquivBytes, 0, 16);
            sb.Append("    " + GetBytesString(stringEquivBytes));
            sb.Append(Environment.NewLine);
        }
    }

    int remainingBytes = i % 16;
    if (remainingBytes != 0)
    {
        byte[] stringEquivBytes = new byte[remainingBytes];
        Array.Copy(dataRead, i - remainingBytes, stringEquivBytes, 0, remainingBytes);
        sb.Append("    ".PadLeft((16 - remainingBytes + 1) * 3 + 1) + GetBytesString(stringEquivBytes));
        //sb.Append(Environment.NewLine);
    }

    return sb.ToString();
}
, with GetBytesString() defined as:
/// <summary>
        /// Return a string representation of binary data
        /// </summary>
        /// <remarks>
        /// Converts valid caracter-bytes into their string equivalent, and replaces non-valid characters with '.'
        /// </remarks>
        private static string GetBytesString(byte[] data)
        {
            for (int i = 0; i < data.Length; i++)
            {
                if (data[i] < 32 /*|| data[i] > 126*/)
                {
                    data[i] = (byte)46;
                }
            }

            return Encoding.ASCII.GetString(data);
        }

No comments:

Post a Comment