代码之家  ›  专栏  ›  技术社区  ›  Matthew Steven Monkan

在一个流类中缝合多个流

  •  2
  • Matthew Steven Monkan  · 技术社区  · 14 年前

    我想做一个班级(我们称之为“班级大集体”),需要 IEnumerable <Stream > 在其构造函数中。这个HugeStream应该实现流抽象类。

    基本上,我有1到多个来自数据库的utf8流,当它们组合在一起时,会形成一个巨大的XML文档。需要对Hugestream进行文件备份,以便我可以随时查找整个缝合在一起的流的位置0。

    有人知道如何快速实现这一点吗?

    我看到类似的东西在 this page 但对于处理大量大型河流来说,这似乎不是最佳选择。效率是关键。

    另一方面,我在可视化流方面遇到了困难,现在我有点困惑,因为我需要实现自己的流。如果有一个很好的关于实现流类的教程,任何人都知道,请告诉我;我没有找到任何好的文章浏览周围。我只看到了很多关于使用已经存在的文件流和内存流的文章。我是一个非常直观的学习者,由于某些原因,我找不到任何有用的东西来研究这个概念。

    谢谢,

    马特

    1 回复  |  直到 14 年前
        1
  •  4
  •   Jason Williams    14 年前

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

    到目前为止,这些都是直接实现的——您只需要间接地将读调用传递到适当的流,并在每个流耗尽数据时切换流。

    这篇引用文章的低效之处在于它贯穿所有的信息流。 每一个 你读到的时间,以确定从何处继续阅读。为了改进这一点,您只需要在需要的时候打开子流,并跟踪当前打开的流,这样您就可以继续从当前流中读取更多的数据,直到用完为止。然后打开下一个流作为您的“当前”流并继续。这是非常直接的,因为你有一个线性的流序列,所以你只需要一个接一个地穿过它们。例如:

    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和缓存之间是相同的-您简化了必须重定向所有读取调用才能将数据从缓存流中取出)

    如果读取或查找到缓存流的末尾,则需要继续从当前子流读取数据。回到上面的逻辑,继续向缓存流追加数据。

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

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

    (您不需要实现更多的read-and-seek接口来实现此功能)。