我正在查看一个旧的助手方法,我已经用它来跟踪字节数组到输出。我很久以前就写过了,它运行得很好,但是我想知道是否有更好的方法(用更少的代码)。Linq突然出现在我的脑海中,但我的解决方案效率极低。我需要的是类似于"foreach16“的东西,或者某个枚举器,它不是一次返回一个元素,而是返回一个可枚举元素的一组元素。除了创建我自己的枚举器类之外,还有内置的方法吗?
下面的例子提供了更多关于我想要完成的任务的信息。
原始代码
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());
}我现在拥有的--这就是我试图简化代码的地方。但是我做了很多跳取,这增加了代码的复杂度,从线性到二次。
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 : '.')));
}注意,即使新代码是线性的,我也可能坚持原来的代码,因为它可以工作;2)我认为它更容易读懂。但是,我不禁想知道,是否有某种方法来迭代组。
发布于 2012-05-21 04:20:42
好吧,我不确定这是否更易读,但是这里有一个解决方案,它使用类似于反应性扩展的Buffer扩展方法。
public static IEnumerable<IList<T>> Buffer<T>(this IEnumerable<T> orig, int count)
{
return orig.Select((o,i) => new { o, i })
.GroupBy(x => x.i / count, x => x.o)
.Select(g => g.ToList());
}给定一个16字节的块,将它们转换为一个字符串(位于每行末尾):
static string FormatAsString(IList<byte> bytes)
{
return String.Join(" ",
bytes.Buffer(8).Select(
bs => new String(bs.Select(b => ' ' <= b && b <= '~' ? (char)b : '.').ToArray())
)
);
}给定一个字节块(通常为16个宽),将它们转换为这些字节的字符串表示形式(在每一行开头):
static string FormatAsBytes(IList<byte> bytes)
{
var blocks =
bytes.Buffer(8)
.Select(bs => String.Join(" ",
bs.Select(b => String.Format("{0:X2}", (int)b)))
);
return String.Join(" ", blocks);
}现在,如果我们将输入字节转换为块,那么我们就可以在输出上运行上面的两个:
static void PrintBytesWide(byte[] bytes, int width)
{
foreach (var line in bytes.Buffer(width))
{
Console.WriteLine("{0} {1}", FormatAsBytes(line).PadRight((width + 1) * 3, ' '), FormatAsString(line));
}
}若要使用16个字节块运行:
var bytes = Encoding.UTF8.GetBytes("the quick brown fox");
PrintBytesWide(bytes, 16);对我来说,这基本上和你原来的输出一样;
原件:
74 68 65 20 71 75 69 63 6B 20 62 72 6F 77 6E 20 the quic k brown
66 6F 78 fox新的:
74 68 65 20 71 75 69 63 6B 20 62 72 6F 77 6E 20 the quic k brown
66 6F 78 fox但当然,美丽的是,你可以做不同的宽度!
PrintBytesWide(bytes, 8);
74 68 65 20 71 75 69 63 the quic
6B 20 62 72 6F 77 6E 20 k brown
66 6F 78 fox
PrintBytesWide(bytes, 24);
74 68 65 20 71 75 69 63 6B 20 62 72 6F 77 6E 20 66 6F 78 the quic k brown fox发布于 2012-05-21 03:56:55
LINQ使代码与您所使用的IEnumerable的类型更加可读性和解耦。但是从抽象的本质来看,它的效率要比手工为您的特定需求编写低级代码的效率要低
发布于 2012-05-21 04:51:39
以下是你所需要的:
var result =
String.Join("\n",
bytes
.Select((b, i) => new { b, i })
.GroupBy(x => x.i / 16, x => x.b)
.Select(bs =>
String.Join(" ",
bs.Select(b =>
String
.Format("{0:X2}", b)))
.PadRight(16 * 3, ' ')));我用“快速棕狐”测试了上面的代码。(使用UTF8)并获得以下输出:
54 68 65 20 71 75 69 63 6B 20 62 72 6F 77 6E 20
66 6F 78 2E 显然,我对我的第一个版本有点草率。这可能会更完整一些。
Func<string, IEnumerable<byte>> toBytes =
x => System.Text.UTF8Encoding.UTF8.GetBytes(x);
Func<IEnumerable<byte>, string> toString =
x => System.Text.UTF8Encoding.UTF8.GetString(x.ToArray());
Func<IEnumerable<byte>, string> toHexBlock =
xs => String.Join(" ", xs.Select(x => String.Format("{0:X2}", x)));
Func<IEnumerable<byte>, string> toHexLine =
xs =>
String
.Format("{0} {1}",
toHexBlock(xs.Take(8)),
toHexBlock(xs.Skip(8).Take(8)))
.PadRight(16 * 3 + 1, ' ')
+ String
.Format("{0} {1}",
toString(xs.Take(8)),
toString(xs.Skip(8).Take(8)));
var result =
String.Join("\n",
toBytes("The even quicker brown fox.")
.Select((b, i) => new { b, i })
.GroupBy(x => x.i / 16, x => x.b)
.Select(bs => toHexLine(bs)));https://stackoverflow.com/questions/10679245
复制相似问题