我在这里挣扎。通常我会读一本书,但还没有。我已经找到了无数的使用RX读取流的各种事情的例子,但我发现很难理解。
我知道我可以使用Observable.FromAsyncPattern来创建Stream的BeginRead / EndRead或BeginReadLine / EndReadLine方法的包装。
但这只会读一次 - 当第一个观察者订阅时。
我想要一个Observable,它将继续读取和抽取OnNext,直到流错误或结束。
除此之外,我还想知道如何与多个订阅者共享该observable,以便他们都能获得这些项目。
答案 0 :(得分:4)
使用rxx添加Lee的答案:
using (new FileStream(@"filename.txt", FileMode.Open)
.ReadToEndObservable()
.Subscribe(x => Console.WriteLine(x.Length)))
{
Console.ReadKey();
}
将输出读缓冲区的长度。
答案 1 :(得分:2)
您可以使用Repeat
继续阅读直到流末尾的行和Publish
或Replay
,以便控制多个读者之间的共享。
一个简单,完整的Rx解决方案的示例,用于从任何流中读取行直到结束:
public static IObservable<string> ReadLines(Stream stream)
{
return Observable.Using(
() => new StreamReader(stream),
reader => Observable.FromAsync(reader.ReadLineAsync)
.Repeat()
.TakeWhile(line => line != null));
}
此解决方案还利用了ReadLine
在到达流末尾时返回null
的事实。
答案 2 :(得分:1)
参考:Reading from NetworkStream corrupts the buffer
在那里,我有一个像这样的扩展方法:
public static class Ext
{
public static IObservable<byte[]> ReadObservable(this Stream stream, int bufferSize)
{
// to hold read data
var buffer = new byte[bufferSize];
// Step 1: async signature => observable factory
var asyncRead = Observable.FromAsyncPattern<byte[], int, int, int>(
stream.BeginRead,
stream.EndRead);
return Observable.While(
// while there is data to be read
() => stream.CanRead,
// iteratively invoke the observable factory, which will
// "recreate" it such that it will start from the current
// stream position - hence "0" for offset
Observable.Defer(() => asyncRead(buffer, 0, bufferSize))
.Select(readBytes => buffer.Take(readBytes).ToArray()));
}
}
您可以使用以下形式编写的内容:
// Note: ToEnumerable works here because your filestream
// has a finite length - don't do this with infinite streams!
var blobboData = stream
.ReadObservable(bufferSize)
// take while we're still reading data
.TakeWhile(returnBuffer => returnBuffer.Length > 0)
.ToEnumerable()
// mash them all together
.SelectMany(buffer => buffer)
.ToArray();
答案 3 :(得分:1)
解决方案是使用Observable.Create
这是一个可以适用于阅读任何类型流的示例
public static IConnectableObservable<Command> GetReadObservable(this CommandReader reader)
{
return Observable.Create<Command>(async (subject, token) =>
{
try
{
while (true)
{
if (token.IsCancellationRequested)
{
subject.OnCompleted();
return;
}
//this part here can be changed to something like this
//int received = await Task.Factory.FromAsync<int>(innerSocket.BeginReceive(data, offset, size, SocketFlags.None, null, null), innerSocket.EndReceive);
Command cmd = await reader.ReadCommandAsync();
subject.OnNext(cmd);
}
}
catch (Exception ex)
{
try
{
subject.OnError(ex);
}
catch (Exception)
{
Debug.WriteLine("An exception was thrown while trying to call OnError on the observable subject -- means you're not catching exceptions everywhere");
throw;
}
}
}).Publish();
}
不要忘记在返回的IConnectableObservable
上调用Connect()