我理解流是字节序列的表示。每个流都提供了将字节读写到其给定的后备存储的方法。但溪流的意义何在?为什么后台存储本身不是我们交互的对象?

不知什么原因,我就是不喜欢这个概念。我读了很多文章,但我觉得我需要一个类比。


当前回答

我所见过的关于流的最好解释是SICP的第3章。(你可能需要阅读前两章才能理解,但无论如何你都应该这样做。: -)

它们对字节根本不使用sterams,而是整数。我从中得到的要点是:

流是延迟列表 在某些情况下,急于提前计算所有内容的计算开销是惊人的 我们可以用流来表示无限长的序列

其他回答

流的目的是在您和后台存储之间提供一个抽象层。因此,使用流的给定代码块不需要关心后台存储是磁盘文件、内存等等…

我长话短说,我刚才漏掉了这个词:

流是通常存储在包含任何类型数据的缓冲区中的队列。

(现在,既然我们都知道队列是什么,就没有必要进一步解释了。)

我认为您需要考虑后备存储本身通常只是另一种抽象。内存流很容易理解,但是文件完全不同,这取决于您使用的文件系统,而不考虑您使用的是什么硬盘驱动器。事实上,并不是所有的流都位于备份存储的顶部:网络流基本上就是流。

流的意义在于我们将注意力限制在重要的事情上。通过标准抽象,我们可以执行公共操作。例如,即使您今天不想在文件或HTTP响应中搜索url,也不意味着您明天就不想这样做了。

Streams were originally conceived when memory was tiny compared to storage. Just reading a C file could be a significant load. Minimizing the memory footprint was extremely important. Hence, an abstraction in which very little needed to be loaded was very useful. Today, it is equally useful when performing network communication and, it turns out, rarely that restrictive when we deal with files. The ability to transparently add things like buffering in a general fashion makes it even more useful.

这只是一个概念,另一个层次的抽象,让你的生活更容易。它们都有共同的接口,这意味着你可以以类似管道的方式组合它们。例如,编码到base64,然后压缩,然后将其写入磁盘,所有这些都在一行中!

流表示可以按顺序访问的对象序列(通常是字节,但不一定是这样)。流的典型操作:

read one byte. Next time you read, you'll get the next byte, and so on. read several bytes from the stream into an array seek (move your current position in the stream, so that next time you read you get bytes from the new position) write one byte write several bytes from an array into the stream skip bytes from the stream (this is like read, but you ignore the data. Or if you prefer it's like seek but can only go forwards.) push back bytes into an input stream (this is like "undo" for read - you shove a few bytes back up the stream, so that next time you read that's what you'll see. It's occasionally useful for parsers, as is: peek (look at bytes without reading them, so that they're still there in the stream to be read later)

一个特定的流可能支持读(在这种情况下,它是一个“输入流”),写(“输出流”)或两者都支持。并不是所有的溪流都是可搜索的。

Push back is fairly rare, but you can always add it to a stream by wrapping the real input stream in another input stream that holds an internal buffer. Reads come from the buffer, and if you push back then data is placed in the buffer. If there's nothing in the buffer then the push back stream reads from the real stream. This is a simple example of a "stream adaptor": it sits on the "end" of an input stream, it is an input stream itself, and it does something extra that the original stream didn't.

Stream is a useful abstraction because it can describe files (which are really arrays, hence seek is straightforward) but also terminal input/output (which is not seekable unless buffered), sockets, serial ports, etc. So you can write code which says either "I want some data, and I don't care where it comes from or how it got here", or "I'll produce some data, and it's entirely up to my caller what happens to it". The former takes an input stream parameter, the latter takes an output stream parameter.

我能想到的最好的比喻是,溪流是一条传送带,向你走来或离开你(有时两者兼而有之)。你从输入流中取出东西,你把东西放到输出流中。有些传送带你可以认为是从墙上的一个洞里出来的——它们是不可寻找的,阅读或写作是一次性的交易。一些传送带就摆在你面前,你可以在溪流中选择你想读/写的位置——这就是寻找。

As IRBMe says, though, it's best to think of a stream in terms of the operations it offers (which vary from implementation to implementation, but have a lot in common) rather than by a physical analogy. Streams are "things you can read or write". When you start connecting up stream adaptors, you can think of them as a box with a conveyor in, and a conveyor out, that you connect to other streams and then the box performs some transformation on the data (zipping it, or changing UNIX linefeeds to DOS ones, or whatever). Pipes are another thorough test of the metaphor: that's where you create a pair of streams such that anything you write into one can be read out of the other. Think wormholes :-)