我正在尝试提高我的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
中执行相同的操作?
编辑:我编辑了上面的问题和代码,试图说清楚我不能一次性覆盖整个字节缓冲区。我很抱歉我一开始并没有说清楚。
答案 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;
}
}
}
}