首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >采用元素“组”的迭代版本

采用元素“组”的迭代版本
EN

Stack Overflow用户
提问于 2012-05-21 03:34:28
回答 3查看 125关注 0票数 0

我正在查看一个旧的助手方法,我已经用它来跟踪字节数组到输出。我很久以前就写过了,它运行得很好,但是我想知道是否有更好的方法(用更少的代码)。Linq突然出现在我的脑海中,但我的解决方案效率极低。我需要的是类似于"foreach16“的东西,或者某个枚举器,它不是一次返回一个元素,而是返回一个可枚举元素的一组元素。除了创建我自己的枚举器类之外,还有内置的方法吗?

下面的例子提供了更多关于我想要完成的任务的信息。

原始代码

代码语言:javascript
复制
    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());
    }

我现在拥有的--这就是我试图简化代码的地方。但是我做了很多跳取,这增加了代码的复杂度,从线性到二次。

代码语言:javascript
复制
    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)我认为它更容易读懂。但是,我不禁想知道,是否有某种方法来迭代组。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-05-21 04:20:42

好吧,我不确定这是否更易读,但是这里有一个解决方案,它使用类似于反应性扩展的Buffer扩展方法。

代码语言:javascript
复制
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字节的块,将它们转换为一个字符串(位于每行末尾):

代码语言:javascript
复制
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个宽),将它们转换为这些字节的字符串表示形式(在每一行开头):

代码语言:javascript
复制
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);
}

现在,如果我们将输入字节转换为块,那么我们就可以在输出上运行上面的两个:

代码语言:javascript
复制
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个字节块运行:

代码语言:javascript
复制
var bytes = Encoding.UTF8.GetBytes("the quick brown fox");
PrintBytesWide(bytes, 16);

对我来说,这基本上和你原来的输出一样;

原件:

代码语言:javascript
复制
 74 68 65 20 71 75 69 63  6B 20 62 72 6F 77 6E 20   the quic k brown 
 66 6F 78                                           fox

新的:

代码语言:javascript
复制
74 68 65 20 71 75 69 63  6B 20 62 72 6F 77 6E 20    the quic k brown 
66 6F 78                                            fox

但当然,美丽的是,你可以做不同的宽度!

代码语言:javascript
复制
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
票数 1
EN

Stack Overflow用户

发布于 2012-05-21 03:56:55

LINQ使代码与您所使用的IEnumerable的类型更加可读性和解耦。但是从抽象的本质来看,它的效率要比手工为您的特定需求编写低级代码的效率要低

票数 1
EN

Stack Overflow用户

发布于 2012-05-21 04:51:39

以下是你所需要的:

代码语言:javascript
复制
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)并获得以下输出:

代码语言:javascript
复制
54 68 65 20 71 75 69 63 6B 20 62 72 6F 77 6E 20 
66 6F 78 2E                                     

显然,我对我的第一个版本有点草率。这可能会更完整一些。

代码语言:javascript
复制
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)));
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10679245

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档