创建Observable的正确方法是什么,该Observable将流读取到最后

时间:2013-01-22 08:58:33

标签: c# system.reactive

我在这里挣扎。通常我会读一本书,但还没有。我已经找到了无数的使用RX读取流的各种事情的例子,但我发现很难理解。

我知道我可以使用Observable.FromAsyncPattern来创建Stream的BeginRead / EndRead或BeginReadLine / EndReadLine方法的包装。

但这只会读一次 - 当第一个观察者订阅时。

我想要一个Observable,它将继续读取和抽取OnNext,直到流错误或结束。

除此之外,我还想知道如何与多个订阅者共享该observable,以便他们都能获得这些项目。

4 个答案:

答案 0 :(得分:4)

使用rxx添加Lee的答案:

using (new FileStream(@"filename.txt", FileMode.Open)
       .ReadToEndObservable()
       .Subscribe(x => Console.WriteLine(x.Length)))
{
  Console.ReadKey();
}

将输出读缓冲区的长度。

答案 1 :(得分:2)

您可以使用Repeat继续阅读直到流末尾的行和PublishReplay,以便控制多个读者之间的共享。

一个简单,完整的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()