c# - 在一个 Stream 类中拼接多个流

标签 c# stream iostream

我想创建一个接受 IEnumerable<code><Stream</code>> 的类(我们称其为 HugeStream 类)在它的构造函数中。这个 HugeStream 应该实现 Stream 抽象类。

基本上,我有 1 到许多来自数据库的 UTF8 流,当它们放在一起时,会形成一个巨大的 XML 文档。 HugeStream 需要以文件为后盾,这样我就可以随时返回到整个拼接流的位置 0。

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

我在 this page 看到了类似的创建但它似乎不是处理大量大流的最佳选择。效率是关键。

附带说明一下,我在可视化 Streams 时遇到了麻烦,现在我有点困惑,因为我需要实现自己的 Stream。如果有人知道关于实现 Stream 类的好的教程,请告诉我;翻来覆去也没发现什么好文章。我刚刚看到很多关于使用现有的 FileStreams 和 MemoryStreams 的文章。我是一个视觉型学习者,出于某种原因找不到任何有用的东西来研究这个概念。

谢谢,

马特

最佳答案

如果您只从 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 接口(interface)之外,您不需要实现更多的功能来实现此功能)。

关于c# - 在一个 Stream 类中拼接多个流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3963483/

相关文章:

c# - 检查字符串是否不等于某物

c# - WPF - 边框和文本 block - 奇怪的行为

c++ - 比较流

c++ - 如何清除异常掩码

python - C++ iostreams 和 python

c++ - 可以插入 fstream,但不能插入 iostream

C# 如何操作一个列表来从不同的字符构建单词?

c# - 脚本任务中的触发事件

Node.js - 如何处理流错误事件

sql-server - 打开从 sql server 下载的 Office 文件时出错