I’m looking at an old helper method which I’ve been using for a while to trace byte arrays to the output. I wrote it a long time ago and it’s been working fine, but I was wondering if there was a better way to do that (with less code). Linq came to my mind, but the solution which I have is awfully inefficient. What I’d need would be something along the lines of a “foreach16”, or some enumerator which instead of returning 1 element at a time, returned a group of elements of an enumerable. Besides me creating my own enumerator class, is there a built-in way of doing that?
The examples below have more information on what I’m trying to accomplish.
Original code
static void PrintBytes(byte[] bytes)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
if (i > 0 && ((i % 16) == 0))
{
// end of line, flushes bytes and resets buffer
Console.WriteLine(" {0}", sb.ToString());
sb.Length = 0;
}
else if (i > 0 && ((i % 8) == 0))
{
Console.Write(" ");
sb.Append(' ');
}
Console.Write(" {0:X2}", (int)bytes[i]);
if (' ' <= bytes[i] && bytes[i] <= '~')
{
sb.Append((char)bytes[i]);
}
else
{
// non-ASCII or control chars are printed as '.'
sb.Append('.');
}
}
// flushes the last few bytes
if ((bytes.Length % 16) > 0)
{
// prints spaces where the missing bytes would be
int spacesToPrint = 3 * (16 - (bytes.Length % 16));
if ((bytes.Length % 16) <= 8)
{
spacesToPrint++;
}
Console.Write(new string(' ', spacesToPrint));
}
Console.WriteLine(" {0}", sb.ToString());
}
What I have now – this is what I tried to simplify the code. But I’m doing many Skip/Take, which increases the complexity of the code from linear to quadratic.
static void PrintBytesV2(byte[] bytes)
{
for (int i = 0; i < bytes.Length; i += 16)
{
PrintLineV2(bytes, i, Math.Min(16, bytes.Length - i));
}
}
static void PrintLineV2(byte[] array, int offset, int count)
{
Console.Write(
string.Join(
" ",
array
.Skip(offset)
.Take(count)
.Select((b, i) =>
((i == 8) ? " " : "") +
string.Format("{0:X2}", (int)b))));
Console.Write(
new string(
' ',
(16 - count) * 3 +
(count <= 8 ? 1 : 0)) +
" ");
Console.WriteLine(
string.Join(
"",
array
.Skip(offset)
.Take(count)
.Select(b => (' ' <= b && b <= '~') ? (char)b : '.')));
}
Notice that even if the new code as it is were linear, I’d likely stick with the original code since 1) it works; and 2) I think it’s more legible. But I can’t help wondering whether there is some way of iterating over groups.
OK well I’m not sure this is more readable, but here’s a solution that uses a
Bufferextension method similar to that from Reactive Extensions.Given a block of 16 bytes, turn them into a string (goes at the end of each line):
Given a block of bytes (usually 16 wide), turn them into a string representation of those bytes (goes at the beginning of each line):
Now if we turn the input bytes into blocks, then we can just run the above two on the output:
To run with 16 byte blocks:
For me this returned basically the same output as your original;
Original:
New:
But of course the beauty is that you can do different widths!