在一个Stream类中拼接多个流

时间:2010-10-18 21:18:12

标签: c# stream iostream

我想创建一个类(让我们调用类HugeStream)在其构造函数中使用IEnumerable<Stream>。这个HugeStream应该实现Stream抽象类。

基本上,我有来自数据库的1到多个UTF8流,当它们组合在一起时,会生成一个巨大的XML文档。 HugeStream需要文件支持,以便我可以随时寻找整个拼接流的位置0。

任何人都知道如何快速实现这个目标吗?

我在this page看到了类似的东西,但它似乎不适合处理大量的大流。效率是关键。

另一方面,我无法直观地看到Streams,因为我需要实现自己的Stream,所以有点困惑。如果有一个关于实现Stream类的好教程,任何人都知道,请告诉我;我没有找到任何浏览的好文章。我刚看到很多关于使用已经存在的FileStreams和MemoryStreams的文章。我是一个非常直观的学习者,由于某种原因,找不到任何有用的东西来研究这个概念。

谢谢,

马特

1 个答案:

答案 0 :(得分:4)

如果你只是从HugeStream中顺序读取数据,那么它只需要读取每个子流(并将其附加到本地文件以及将读取数据返回给调用者),直到子流耗尽,然后继续前进到下一个子流。如果使用Seek操作在数据中“向后”跳转,则必须从本地缓存文件开始读取;当你到达缓存文件的末尾时,你必须继续读取你离开的当前子流。

到目前为止,这一切都非常直接实现 - 您只需要将Read调用间接到适当的流,并在每个数据耗尽时切换流。

所引用文章的低效率是它会在您阅读的每个时间内遍历所有流,以确定从何处继续阅读。要改进这一点,您需要仅在需要时打开子流,并跟踪当前打开的流,以便您可以继续从当前流中读取更多数据,直到它耗尽为止。然后打开下一个流作为“当前”流并继续。这是非常简单的,因为你有一个线性的流序列,所以你只需要逐个浏览它们。例如:

int currentStreamIndex = 0;
Stream currentStream = childStreams[currentStreamIndex++];

...

public override int Read(byte[] buffer, int offset, int count)
{
    while (count > 0)
    {
        // Read what we can from the current stream
        int numBytesRead = currentSteam.Read(buffer, offset, count);
        count -= numBytesRead;
        offset += numBytesRead;

        // If we haven't satisfied the read request, we have exhausted the child stream.
        // Move on to the next stream and loop around to read more data.
        if (count > 0)
        {
            // If we run out of child streams to read from, we're at the end of the HugeStream, and there is no more data to read
            if (currentStreamIndex >= numberOfChildStreams)
                break;

            // Otherwise, close the current child-stream and open the next one
            currentStream.Close();
            currentStream = childStreams[currentStreamIndex++];
        }
    }

   // Here, you'd write the data you've just read (into buffer) to your local cache stream
}

为了允许向后搜索,您只需要引入一个新的本地文件流,您可以在阅读时将所有数据复制到该文件流中(请参阅上面我的伪代码中的注释)。您需要引入一个状态,以便知道您正在读取缓存文件而不是当前子流,然后直接访问缓存(寻求等很容易,因为缓存代表从HugeStream读取的数据的整个历史记录) ,因此HugeStream和Cache之间的搜索偏移是相同的 - 您只需重定向任何Read调用以从缓存流中获取数据)

如果您读取或寻找缓存流的末尾,则需要继续从当前子流中读取数据。只需返回上面的逻辑并继续将数据附加到缓存流。

如果您希望能够支持HugeStream中的完全随机访问,则需要支持寻找“转发”(超出缓存流的当前末尾)。如果您事先不知道子流的长度,则别无选择,只需将数据读入缓存,直到达到搜索偏移量为止。如果您知道所有流的大小,那么您可以直接并更有效地寻找到正确的位置,但是您必须设计一种有效的方法将您读取的数据存储到缓存文件并记录缓存的哪些部分包含有效数据,但实际上尚未从数据库中读取数据 - 这有点高级。

我希望这对你有意义并让你更好地了解如何继续......

(你不需要实现比Read和Seek接口更多的工作来实现这一点。)