LINQ - 除以一系列以null结尾的字符串

时间:2017-05-12 20:18:31

标签: c# linq

我正在尝试提高我的LINQ技能,所以我设计了这个练习。假设我有一个以空字符结尾的字符串的字节流(IEnumerable<byte>)(我不知道流何时结束)。如何使用LINQ来划分字符串并迭代它们?

以下是一些示例代码:

    /// <summary>
    /// A stream of null-terminated random strings of random length.
    /// </summary>
    static IEnumerable<byte> ByteStream
    {
        get
        {
            var rand = new Random();
            while (true)
            {
                for (var i = rand.Next(2, 10); i > 0; --i)
                {
                    yield return (byte) rand.Next(97, 122);
                }
                yield return 0;
            }
        }
    }

    /// <summary>
    /// Divides the sequences of null-terminated strings.
    /// </summary>
    static IEnumerable<IEnumerable<byte>> DividedStream
    {
        get
        {
            var word = new List<byte>();
            foreach (var b in ByteStream)
            {
                if (b == 0)
                {
                    yield return word;
                    word = new List<byte>();
                }
                else
                {
                    word.Add(b);
                }
            }
        }
    }

    static void Main(string[] args)
    {
        foreach (var d in DividedStream)
        {
            Console.WriteLine(Encoding.UTF8.GetString(d.ToArray()));
        }
    }

您会注意到我实际上并没有使用LINQ来划分字符串:DividedStream属性本质上是我自己定制的状态机。看起来我应该能够使用TakeWhile(e => e != 0)来帮助我,但在这种情况下我似乎无法弄清楚如何使用它。

您是否认为我可以删除DividedString属性并使用LINQ单行程在我的main中执行相同的操作?

编辑:我编辑了上面的问题和代码,试图说清楚我不能一次性覆盖整个字节缓冲区。我很抱歉我一开始并没有说清楚。

3 个答案:

答案 0 :(得分:2)

我真的不认为你可以单行LINQ转换,但滥用Aggregate总是很有趣:

var ans = ByteStream.Aggregate(new List<List<byte>>(), (sa, b) => {
    if (b == 0 || sa.Count == 0)
        sa.Add(new List<byte>());
    if (b != 0)
        sa[sa.Count-1].Add(b);

    return sa;
});

foreach (var d in ans.Select(bl => Encoding.UTF8.GetString(bl.ToArray())))
    Console.WriteLine(d);

当然,您只需使用StringBuilder

var ans = ByteStream.Aggregate(new List<StringBuilder>(), (sa, b) => {
    if (b == 0 || sa.Count == 0)
        sa.Add(new StringBuilder());
    if (b != 0)
        sa[sa.Count - 1].Append((char)b);

    return sa;
}).Select(sb => sb.ToString());

foreach (var d in ans)
    Console.WriteLine(d);

另一种选择是使用GroupBy,但它们都会占据整个来源。

var wc = 0;
var ans = ByteStream.Select(b => new { b, wi = (b == 0) ? wc++ : wc }).GroupBy(bwi => bwi.wi, bwi => bwi.b, (wi, bs) => Encoding.ASCII.GetString(bs.ToArray()));

答案 1 :(得分:0)

public static void Main(string[] args)
    {
        foreach(var d in DividedStream)
            Console.WriteLine(Encoding.UTF8.GetString(d.ToArray()));

        Console.WriteLine("new version:\n");
        Console.WriteLine(string.Concat(ByteStream.Select(x => x.Equals(0) ? Environment.NewLine : Encoding.UTF8.GetString(new[] {x}))));
        Console.ReadKey();
    }

这不完美,因为每个字母都有Encoding.UTF8,但我认为你可以从那开头

修改

这个更短但不使用UTF8

Console.WriteLine(ByteStream.Select(x => x.Equals(0) ? '\n' : (char)x).ToArray());

答案 2 :(得分:0)

我认为这有效:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

namespace ConsoleApplication50
{
    class Program
    {

        static void Main(string[] args)
        {
            IEnumerable<byte> bytes = ByteStream;

            byte[] temp = null;
            int position = 0;
            Console.WriteLine("Count = '{0}'", bytes.Count());
            do 
            {
                temp =    bytes.Skip(position).TakeWhile(b => b != 0).ToArray();
                position += temp.Length + 1; 
                Console.WriteLine("position {0} : {1}", position,string.Join("",temp.Select(x => x.ToString("X2"))));

            } while (position < bytes.Count()) ;


        }



        /// <summary>
        /// A random number of null-terminated random strings of random length. The final
        /// string is just a null terminator (empty string).
        /// </summary>
        static IEnumerable<byte> ByteStream
        {
            get
            {
                var rand = new Random();
                for (var i = rand.Next(64, 128); i != 0; i--)
                {
                    for (var j = rand.Next(2, 10); j != 0; j--)
                    {
                        yield return (byte)rand.Next(97, 122);
                    }
                    yield return 0;
                }
                yield return 0;
            }
        }
    }
}