Answers:
流表示对象序列(通常为字节,但不一定如此),可以按顺序访问。流上的典型操作:
特定的流可能支持读取(在这种情况下,它是“输入流”),写入(“输出流”)或两者兼而有之。并非所有流都是可搜索的。
推回非常少见,但是您始终可以通过将实际输入流包装在另一个具有内部缓冲区的输入流中来将其添加到流中。读取来自缓冲区,如果您往回推,则会将数据放入缓冲区。如果缓冲区中没有任何内容,则推回流将从实际流中读取。这是“流适配器”的一个简单示例:它位于输入流的“末端”,它本身就是输入流,并且做了其他原始流没有做的事情。
Stream是一个有用的抽象,因为它可以描述文件(实际上是数组,因此查找很简单),而且可以描述终端输入/输出(除非经过缓冲,否则是无法查找的),套接字,串行端口等。因此,您可以编写如下代码或者“我想要一些数据,我不在乎它来自何处或如何到达这里”,或者“我将产生一些数据,而发生什么事情完全取决于呼叫者”。前者采用输入流参数,后者采用输出流参数。
我能想到的最好的类比是,一条小溪是一条传送带,朝向您或远离您(或有时两者)。您将内容从输入流中删除,将内容放到输出流中。您可能会认为某些输送机是从墙壁上的孔中出来的-它们是不易寻找的,读或写是一次性的交易。一些传送带布置在您的前面,您可以在要读取/写入的信息流中选择下落。
但是,正如IRBMe所说,最好从流提供的操作(因实现而异,但有很多共同点)的角度来考虑流,而不是通过物理类比来考虑。流是“您可以读取或写入的内容”。当您开始连接流适配器时,您可以将它们看作是一个带有传送器和一个传送器的盒子,您将它们连接到其他流,然后该盒子对数据进行一些转换(压缩它或更改UNIX换行符)到DOS等等。管道是对隐喻的另一种彻底的检验:管道是创建一对流的地方,这样您写入其中一个的任何内容都可以从另一个中读出。想虫洞:-)
流已经是一个隐喻,是一个比喻,因此实际上不需要摆放另一个。您基本上可以将其视为其中有水流的管道,其中水实际上是数据,而管道是水流。如果流是双向的,我想这是一种2通管道。基本上,这是一种常见的抽象概念,它放置在一个方向或两个方向上都有数据流或数据序列的事物上。
在诸如C#,VB.Net,C ++,Java等语言中,流隐喻用于很多事情。有一些文件流,您可以在其中打开文件,并且可以从文件流中读取或连续写入文件。在某些网络流中,对流的读取和写入会从基础的已建立网络连接中进行读取和写入。在此示例中,仅用于写入的流通常称为输出流,类似地,仅用于读取的流也称为输入流,例如这例子。
一个流可以执行数据的转换或编码(例如,.Net中的SslStream将吞噬SSL协商数据并将其隐藏给您; TelnetStream可能会向您隐藏Telnet协商,但提供对数据的访问; A Java中的ZipOutputStream允许您写入zip存档中的文件,而不必担心zip文件格式的内部。
您可能会发现的另一件事是文本流,它允许您编写字符串而不是字节,或者某些语言提供了二进制流,您可以编写原始类型。您会在文本流中发现的常见内容是字符编码。
如本例所示,某些流还支持随机访问。另一方面,出于明显的原因,网络流则不会。
UNIX等操作系统也支持与程序的输入和输出的流模型,如所描述这里。
到目前为止给出的答案是非常好的。我只提供另一个来强调流不是字节序列或特定于编程语言,因为该概念是通用的(尽管其实现可能是唯一的)。我经常在网上看到大量有关SQL或C或Java的解释,当文件流处理内存位置和低级操作时,这是很有意义的。但是他们经常解决如何使用给定语言创建文件流和对潜在文件进行操作,而不是讨论流的概念。
如前所述,a stream
是一个隐喻,是对更复杂事物的抽象。为了发挥您的想象力,我提供了其他一些隐喻:
软管是溪流
软管,喷嘴和相关的机制可以使气体流入油箱
高速公路就是溪流
你的耳朵和眼睛是溪流
希望您在这些示例中注意到,流隐喻仅存在于允许某些事物通过它的情况下(或者在高速公路的情况下),并且它们本身并不总是构成它们正在传递的事物。一个重要的区别。我们不是把耳朵说成是一个单词序列。如果没有水流过,软管仍然是软管,但是我们必须将其连接到水龙头才能正确完成其工作。汽车并不是唯一可以穿越高速公路的“种类”车辆。
因此,一个流可以存在没有数据通过它,只要它行驶连接到一个文件。
接下来,我们需要回答一些问题。我将使用文件来描述流,因此...什么是文件?以及我们如何读取文件?我将尝试在维持一定抽象水平的同时回答这个问题,以避免不必要的复杂性,并且由于其简单性和可访问性,将使用相对于Linux操作系统的文件概念。
文件是抽象的:)
或者,正如我可以简单解释的那样,文件是描述文件的一部分数据结构,而一部分是实际内容。
数据结构部分(在UNIX / linux系统中称为inode)标识有关内容的重要信息,但不包括内容本身(或与此相关的文件名)。它保留的信息之一是内容开始的内存地址。因此,有了文件名(或Linux中的硬链接),文件描述符(操作系统关心的数字文件名)和内存中的起始位置,我们便有了可以称为文件的名称。
(关键要点是操作系统定义的“文件”,因为最终要处理该文件的是操作系统。是的,文件要复杂得多)。
到目前为止,一切都很好。但是,我们如何获取文件的内容,给您的爱人说一封情书,以便我们进行打印?
如果从结果开始并向后移动,则当我们在计算机上打开文件时,其所有内容都会溅到屏幕上供我们阅读。但是如何?答案很有条理。文件本身的内容是另一个数据结构。假设一个字符数组。我们也可以将其视为字符串。
那么我们如何“读取”该字符串?通过找到它在内存中的位置并遍历我们的字符数组,一次可以一个字符直到到达文件末尾。换句话说就是一个程序。
当流的程序被调用并且具有可连接或连接到的内存位置时,将 “创建”流。就像我们的水管示例一样,如果未连接水龙头,则该水管无效。对于流,必须将其连接到文件才能存在。
流可以进一步完善,例如,用于接收输入的流或用于将文件内容发送到标准输出的流。UNIX / linux可以立即为我们连接并保持开放的3个文件流,stdin(标准输入),stdout(标准输出)和stderr(标准错误)。流可以构建为数据结构本身或对象,这使我们可以通过它们执行数据流的更复杂的操作,例如打开流,关闭流或对流连接到的文件进行错误检查。C ++ cin
是流对象的示例。
当然,如果您选择这样做,则可以编写自己的流。
流是可重用的代码段,它抽象了处理数据的复杂性,同时提供了对数据执行的有用操作。
另一个比喻:您不能在流中游动,这就是为什么您可以从流中获取下一个位,字节,字符串或对象,同时删除已读取的数据的原因。单程票...或基本上只是一个队列而没有存储持久性。
那么我们需要队列吗?你决定。