web-dev-qa-db-ja.com

ストリームとは何ですか?

プログラミングの世界のストリームとは何ですか?なぜ必要なのですか?

可能であれば、類推の助けを借りて親切に説明してください。

110
pokrate

ストリームは一連のオブジェクト(通常はバイトですが、必ずしもそうである必要はありません)を表し、順番にアクセスできます。ストリームの典型的な操作:

  • 1バイトを読み取ります。次回読むときには、次のバイトを取得します。
  • ストリームから数バイトを配列に読み込みます
  • シーク(ストリーム内の現在の位置を移動し、次に読むときに新しい位置からバイトを取得できるようにします)
  • 1バイトを書く
  • 配列からストリームに数バイトを書き込みます
  • ストリームからバイトをスキップします(これは読み取りに似ていますが、データを無視します。または、必要に応じてシークになりますが、前方にしか移動できません)
  • 入力ストリームにバイトをプッシュバックします(これは読み取りの「元に戻す」に似ています-ストリームを数バイト戻すと、次に読むときに表示されます。これは、パーサーに役立つことがあります。
  • 覗く(バイトを読み取らずに見て、後で読み取るストリームに残っているようにする)

特定のストリームは、読み取り(この場合は「入力ストリーム」)、書き込み(「出力ストリーム」)、またはその両方をサポートします。すべてのストリームがシーク可能というわけではありません。

プッシュバックはかなりまれですが、実際の入力ストリームを内部バッファーを保持する別の入力ストリームにラップすることで、いつでもストリームに追加できます。読み取りはバッファから行われ、プッシュバックするとデータがバッファに配置されます。バッファに何もない場合、プッシュバックストリームは実際のストリームから読み込みます。これは、「ストリームアダプタ」の簡単な例です。入力ストリームの「終わり」に位置し、入力ストリームそのものであり、元のストリームにはなかった特別なことを行います。

ストリームは、ファイル(実際には配列であるためシークが簡単)を記述することができるため便利な抽象化ですが、端末の入出力(バッファリングしない限りシークできない)、ソケット、シリアルポートなども記述できます。 「データが欲しいので、どこから来たのか、どうやってここに来たのかは気にしません」、「データを生成しますが、それは何が起こるかは完全に呼び出し元次第」です。前者は入力ストリームパラメータを受け取り、後者は出力ストリームパラメータを受け取ります。

私が考えることができる最高の例えは、ストリームがあなたに向かってくる、またはあなたから離れる(あるいはその両方)コンベアベルトであるということです。入力ストリームからものを取り出し、出力ストリームにものを置きます。壁の穴から出てくると考えることができるいくつかのコンベヤー-それらはシーク可能ではなく、読み取りまたは書き込みは一度限りの取引です。いくつかのコンベアが目の前に配置されており、読み/書きたいストリームの場所を選択しながら移動できます。

ただし、IRBMeが言うように、物理的な類推ではなく、提供する操作(実装によって異なりますが、共通点が多い)の観点からストリームを考えるのが最善です。ストリームは「読み取りまたは書き込みが可能なもの」です。ストリームアダプターの接続を開始すると、他のストリームに接続し、そのボックスがデータに何らかの変換(圧縮、またはUNIXラインフィードの変更)を実行する、コンベヤーが入ったボックスとコンベヤーが出たボックスと考えることができますDOSのもの、または何に)。パイプは、メタファーのもう1つの完全なテストです。ここでは、1組のストリームを作成して、一方に書き込んだものをもう一方から読み取れるようにします。ワームホールだと思う:-)

136
Steve Jessop

ストリームはすでに比phorであり、類推であるため、実際に別のストリームを検討する必要はありません。基本的には、水が実際にデータであり、パイプがストリームである、水の流れがあるパイプと考えることができます。ストリームが双方向の場合、2ウェイパイプのようなものだと思います。これは基本的に、一方向または双方向にデータのフローまたはシーケンスがある場合に配置される共通の抽象化です。

C#、VB.Net、C++などの言語では、Javaなど)、ストリームメタファーは多くのことに使用されます。ファイルを開いて、ストリームへの連続的な書き込みまたは書き込み;ストリームの読み取りと書き込みが、確立されたネットワーク接続との間で読み取りおよび書き込みを行うネットワークストリームがあります。書き込み専用のストリームは、通常、出力ストリームと呼ばれます this 例、および同様に、読み取り専用のストリームは this の例のように入力ストリームと呼ばれます。

ストリームは、データの変換またはエンコードを実行できます(たとえば、.Netの SslStream は、SSLネゴシエーションデータを使い果たし、それを隠します。TelnetStreamは、Telnetネゴシエーションを隠しますが、データへのアクセスを提供します; A ZipOutputStream in Javaを使用すると、Zipファイル形式の内部を心配することなく、Zipアーカイブ内のファイルに書き込むことができます。

別の一般的なものは、バイトの代わりに文字列を記述できるテキストストリームです。または、一部の言語では、プリミティブ型を記述できるバイナリストリームが提供されます。テキストストリームでよく見かけるのは、文字エンコーディングです。

this の例のように、一部のストリームはランダムアクセスもサポートしています。一方、ネットワークストリームは明らかな理由でそうではありません。

  • [〜#〜] msdn [〜#〜] は、.Netのストリームの概要を示します。
  • Sunには、一般的な OutputStream クラスおよび InputStream クラスの概要もあります。
  • C++では、 istream (入力ストリーム)、 ostream (出力ストリーム)、および iostream (双方向ストリーム)のドキュメントがあります。

UNIXのようなオペレーティングシステムは、 here で説明されているように、プログラムの入出力を使用したスト​​リームモデルもサポートしています。

57
IRBMe

これまでに与えられた答えは素晴らしいです。概念は普遍的であるため(実装は一意である可能性があるため)、ストリームがバイトシーケンスまたはプログラミング言語に固有のものではないことを強調するために、別のものを提供しています。多くの場合、SQL、またはCまたはJavaの観点から多くの説明がオンラインで見られます。これは、ファイルストリームがメモリの場所と低レベルの操作を処理するので理にかなっています。しかし、彼らは多くの場合、ストリームの概念を議論するのではなく、ファイルストリームを作成し、与えられた言語で潜在的なファイルを操作する方法に取り組みます。

メタファー

前述のように、streamは比phorであり、より複雑なものの抽象化です。あなたの想像力を働かせるために、私は他の比phorをいくつか提供します:

  1. 空のプールを水で満たしたい。これを達成する1つの方法は、ホースを栓に取り付け、ホースの端をプールに入れて水を入れることです。

ホースはストリームです

  1. 同様に、車にガソリンを補充したい場合は、ガソリンポンプに行き、ノズルをガソリンタンクに挿入し、ロックレバーを絞ってバルブを開きます。

ガスをタンクに流入させるためのホース、ノズル、および関連するメカニズムはストリームです

  1. 仕事に取り掛かる必要がある場合は、高速道路を使用して自宅から職場まで運転を開始します。

高速道路はストリームです

  1. 誰かと会話したい場合は、耳で聞き、口で話します。

耳と目はストリームです

これらの例で、ストリームのメタファーは何か(または高速道路の場合はその上)を通過することを許可するためだけに存在し、転送するもの自体を常に提示するわけではないことを願っています。重要な区別。私たちは耳を言葉の連続とは呼びません。ホースは、水が流れていない場合でもホースのままですが、正しく機能するためには、蛇口に接続する必要があります。高速道路を横断できる車両は車だけではありません。

したがって、fileconnectedである限り、データが流れないストリームが存在する可能性があります。

抽象化の削除

次に、いくつかの質問に答える必要があります。ファイルを使用してストリームを記述します...ファイルとは何ですか?そして、どのようにファイルを読むのですか?不要な複雑さを避けるために、ある程度の抽象化を維持しながらこれに答えようとし、そのシンプルさとアクセシビリティのために、Linuxオペレーティングシステムに関連するファイルの概念を使用します。

ファイルとは何ですか?

ファイルは抽象化です:)

または、簡単に説明できるように、ファイルは、ファイルを記述する1つの部分のデータ構造と、実際のコンテンツである1つの部分のデータです。

データ構造部分(UNIX/Linuxシステムではiノードと呼ばれます)は、コンテンツに関する重要な情報を識別しますが、コンテンツ自体(またはファイル名)は含まれません。保持する情報の1つは、コンテンツが開始するメモリアドレスです。そのため、ファイル名(またはLinuxのハードリンク)、ファイル記述子(オペレーティングシステムが気にする数値ファイル名)、およびメモリ内の開始位置を使用して、ファイルと呼ぶことができます。

(重要なポイントは「ファイル」であり、OSが最終的にそれを処理する必要があるため、オペレーティングシステムによって定義されます。はい、filesははるかに複雑です)。

ここまでは順調ですね。しかし、どのようにしてファイルの内容を取得し、ラブレターをあなたの愛人に言って、それを印刷できますか?

ファイルを読む

結果から始めて後方に移動すると、コンピューターでファイルを開くと、その内容全体が画面に飛び散って読みやすくなります。しかし、どのように?答えは非常に系統的です。ファイル自体のコンテンツは別のデータ構造です。文字の配列を想定します。これは文字列と考えることもできます。

それでは、この文字列をどのように「読み取る」のでしょうか?メモリ内でその場所を見つけ、文字の配列を繰り返し処理することにより、ファイルの終わりの文字に達するまで一度に1文字ずつ。言い換えれば、プログラム。

ストリームは、そのプログラムが呼び出され、attach toまたはconnect toへのメモリ位置を持つときに「作成」されます。水ホースの例と同様に、ホースは栓に接続されていないと効果がありません。ストリームの場合、存在するにはファイルに接続する必要があります。

ストリームをさらに洗練することができます。たとえば、入力を受信するストリームや、ファイルの内容を標準出力に送信するストリームです。 UNIX/linuxは、すぐに3つのファイルストリームを接続し、そのままにしておきます。stdin(標準入力)、stdout(標準出力)、stderr(標準エラー)です。ストリームは、データ構造自体またはオブジェクトとして構築できます。これにより、ストリームを開く、ストリームを閉じる、ストリームが接続されているファイルをエラーチェックするなど、データストリーミングのより複雑な操作を実行できます。 C++のcinは、ストリームオブジェクトの例です。

確かに、あなたがそう選ぶなら、あなたはあなた自身のストリームを書くことができます。

定義

ストリームは、データを処理するための便利な操作を提供しながら、データ処理の複雑さを抽象化する再利用可能なコードです。

7
rekurzion

上記のものに加えて、SchemeやHaskellなどの関数型プログラミング言語で定義されているような、オンデマンドで何らかの関数によって生成される可能性のある無限のデータ構造の異なる種類のストリームがあります。

6
EFraim

別のアナロジー:ストリームに対して泳ぐことはできません。そのため、既に読み取られたデータが削除されている間に、ストリームから次のビット、バイト、文字列、またはオブジェクトを取得できます。一方向チケット...または基本的には、永続性を保存せずにqueueのみ。

それでは、キューが必要ですか?あなたが決める。

5
Marcus

「ストリーム」という言葉が選ばれたのは、それが(実際に)それを使用するときに伝えたいものと非常に似た意味を表すからです。

水流との類推について考え始めます。川を水が連続して流れるように、データの連続的な流れを受け取ります。データがどこから来たのかを必ずしも知る必要はありません。ほとんどの場合、その必要はありません。ファイル、ソケット、またはその他のソースからのものであっても、実際には問題ではありません(すべきではありません)。これは水の流れを受け取るのと非常に似ており、水がどこから来ているのかを知る必要はありません。それは湖、噴水、または他のソースからのものであっても、それは実際には問題ではありません(すべきではありません)。 ソース

4
Premraj