【问题标题】:fail to read it all into memory when shortcount reading短计数读取时未能将其全部读入内存
【发布时间】:2019-11-19 13:56:41
【问题描述】:

我的课本上说:

Read 从流中接收一个数据块到一个数组中。它返回的数量 接收到的字节数,总是小于或等于 count 参数。如果 它小于计数,这意味着要么已经到达流的末尾,要么 流以较小的块为您提供数据(通常是网络流的情况)。在任何一种情况下,数组中的字节余额都将保持未写入状态,它们之前的值将被保留。 使用 Read,您可以确定您已经到达终点 仅当方法返回 0 时才流式传输。因此,如果您有 1000字节流,下面的代码可能读不全 进入记忆:

// Assuming s is a stream:
byte[] data = new byte [1000];
s.Read (data, 0, data.Length);

Read 方法可以读取 1 到 1,000 个字节的任意位置, 保持流的余额未读。

我很困惑,我写了一个简单的 concolse 应用程序来验证:

//read.txt only contains 3 chars: abc
using (FileStream s = new FileStream("read.txt", FileMode.Open))
{
   byte[] data = new byte[5];
   int num = s.Read(data, 0, data.Length);
   foreach (byte b in data)
   {
      Console.WriteLine(b);
   }
}

输出是:

97

98

99

0

0

所以数据数组已经写入了,为什么教科书上说“数组中字节的余额会保持未写入,可能无法全部读入内存”,

编辑:请丢弃我以前的控制台应用程序,我真正的问题是:

一个文件有 1000 个字节,我请求读取所有 1000 个字节:

byte[] data = new byte [1000];
s.Read (data, 0, data.Length);

为什么它可能无法将其全部读入内存?并且教科书还提供了读取全部1000字节的正确方法:

byte[] data = new byte [1000];
int bytesRead = 0;
int chunkSize = 1;
while (bytesRead < data.Length && chunkSize > 0)
 bytesRead +=
 chunkSize = s.Read (data, bytesRead, data.Length - bytesRead);

【问题讨论】:

  • 你可以注意到s.Read返回一个整数——读取的字节数
  • 斯坦是正确的。大多数粗体文本 (It returns) 都引用了num。总而言之,这意味着'如果您要求读取 1000 个字节,您将获得最多 1000 个字节 - 如果文件 > 1000 个字节,您将无法获得整个文件,并且您如果文件小于 1000 字节,则将获得小于 1000 字节的数据。这就是 bold 文本的关键所在。当它谈到balance of bytes 时,它意味着你看到的那些0。查看它们如何为 0(默认值)。这实际上是不成文的。该数据并非来自文件。
  • 如果您在流上调用Read,则不能保证您可以立即从流中读取所有字节(实际上在大多数情况下您不能)。读取的字节数可能小于缓冲区大小,使缓冲区的其余部分未写入(在您的情况下,数组中的尾随零)。因此,您应该通过循环读取的字节来提前读取起点。
  • stackoverflow.com/questions/3966389/… 可能值得一读@slowjams。
  • @slowjams 因为Read 可以一次只读取一个字节。这将是其他派生类对Stream.Read完全有效的实现 - 尝试接口/抽象类的一种实现不会立即显示所有其他实现的行为方式。

标签: c# .net io


【解决方案1】:

关于:

byte[] data = new byte[5];
int num = s.Read(data, 0, data.Length);
foreach (byte b in data)

foreach 将处理 data 中的每一个字节(并且无论Read() 调用写入了多少个字节,总是五个)。

Read() 调用实际读取的字节数被存储到num 变量中,所以这就是你应该用来处理数据的东西,比如(未经测试但可能很简单,不值得这样做):

for (int i = 0; i < num; i++) {
    Console.WriteLine(data[i]);
}

根据您引用的文本,读取的字节之后的所有内容都可以是任意值,因为 Read() 调用没有更改它们。在您的情况下,它们显然在 Read() 之前为零,因此仍然如此。

如果您要在 Read() 之前和之后运行 foreach 循环,您应该只会看到前三个字节发生变化(当然,假设文件的内容与内存中的内容不同) .

【讨论】:

    【解决方案2】:

    如果它小于计数,则意味着要么已经到达流的末尾,要么流正在以较小的块为您提供数据(通常是网络流的情况)。在任何一种情况下,数组中的剩余字节都不会被写入,它们之前的值会被保留。

    当文本说In either case 时,它指的是If it’s (the return value, i.e the bytes read from the stream) less than count。在这种情况下,读取的字节不会占用您为读取操作指定的所有缓冲区。当然,缓冲区的其余部分保持不变 (remain unwritten, their previous values preserved)。

    在您的情况下,是 the end of the stream has been reached,因为您的文件流中只有 3 个字节。数组中的尾随零是未触及的缓冲区元素。

    所以,如果你有一个 1000 字节的流,下面的代码可能无法将它全部读入内存。

    文本谈论的是一个 1,000 字节的流,而您的流只包含 3 个字节。对于只有 3 个字节的短文件流,很可能所有数据都会一次加载到内存中,这就是您观察到的情况。 (只是“可能”,而不是“保证”)。对于更长的流(或其他类型的流,例如NetworkStream),您不能保证一次读取足够多的字节来填充您指定的缓冲区(有可能您一次只能获得 1 个字节!) .

    【讨论】:

    • 感谢您的回答。我理解第一部分,但是对于第二部分,这是否意味着流有超时机制?假设一个流需要读取 1000 字节,但是当前网络很慢,只提供 500 字节并且处于挂起状态,并且发生超时,所以只有 500 字节被读取到缓冲区中?
    • @slowjams 在网络上,数据到达名为Packet 的单元中。这就像在您的信箱中接收邮件一样。您很少等到邮箱(缓冲区)已满才取出邮件阅读。
    • 感谢类似的例子。我想我从根本上误解了某事。当我们说一个 1000 字节的流时,由于一个流每次只能读取一个字节,这是否意味着该流已经从数据源读取了 1000 次?你能把流的概念放在你的邮箱例子中吗?
    • when we say a 1,000-byte stream, since a stream can only read one byte per time, does it mean that the stream has read 1000 times from data source? @slowjams 坦率地说,你为什么关心它是如何工作的?您需要做的是确保读取num 的值以查看读取了多少字节。其他所有内容都是您无法控制的实现细节,将来可能会发生变化。
    • @mjwills 感谢您的链接。所以我可以说对于 FileStream,永远不会有短计数,如果我想一次从文件中读取 1,000 个字节,我总是会得到num 等于 1000?短计数只会发生在网络流中?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-25
    • 2015-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多