ストリーム(stream)とはどういうことですか

4250 ワード

--この問題に対する考え方は、先日のJava Socketプログラミングの試みに由来しており、TCPプロトコルはSocket接続(有名な3回の握手)を確立してから通信を行うことを要求しているが、接続双方がデータの送信と受信を行うのは、入出力ストリームのメカニズムによって行われている.
フローの概念
ストリームは概念として言語に関係ないはずだ.ファイルIOストリーム、Unixシステム標準入出力ストリーム、標準エラーストリーム(stdin、stdout、stderr)、および最初に述べたTCPストリーム、およびNodejsなどのWebバックグラウンド技術によるHTTP要求/応答ストリームの抽象化は、ストリームの概念を見ることができる.
K&RはC Programming Language書でストリームをこのように定義している.
ストリーム(stream)は、ディスクまたは他の周辺デバイスに関連付けられたデータのソースまたは宛先です.
ストリームはプログラムと外部とデータを交換する抽象的なものと理解することができ、ここでの外部限定は必要であり、通常はプログラム内部のデータストリームをストリームとして抽象化することはできない.結局、プログラム内部では、データストリームは関数によって呼び出され、返されて完成する.3つの標準IOストリームを使用するとき、私たちはどのように外部と対話するかに関心を持っています.ファイル・ストリームを使用する場合、メモリ内のデータをディスク・ファイルに永続化(またはディスクからデータを読み出すメモリ)することに関心があります.
すると、データはAからBへ「流れ」し、水の流れのように高いところから低いところへ流れることができる.水流の過程において,最も基本的な物理組成単位である水分子は不変であり,対応するデータ流にもその最小組成単位がある.異なるプログラミング言語では、この最小単位は、通常、バイトストリーム(バイナリストリーム)内のバイト、または文字ストリーム(テキストストリーム)内の文字である.
-しかし、他のデータ型ではありません.デジタルストリームを聞いたことがありませんか.あるいは浮動小数点数の流れ、甚だしきに至っては配列の流れ?
バイトはコンピュータがデータを保存する最終形式であり、文字は他のデータ構造がシーケンス化された表現形式であり、人が読むことができる形式でもあるからだ.外部とのインタラクションには、これらの一般的なフォーマットが必要です.データの内容に関心がなく、元のデータを完全に転送する場合、バイトストリームを考慮すればよい.文字や文字列の転送に関心を持つ場合は、文字ストリームを操作する必要があります.stdio.hヘッダファイルの大きな入出力関数はこれです.例えば、fgetc(FILE *stream)は、テキストストリームから1文字を読み込む.
一方,データフローの方向に応じて,出力フローと入力フローの概念を再抽象化することができる.プログラム内部から外部への流れは出力ストリームであり,プログラム外部から内部への流れは入力ストリームである.
C言語のstdio.hライブラリには、ファイルストリームを開く際に指定しなければならない集中的な開き方が定義されており、"r"は読み取り用、"w"は書き込み用、"r+"は読み書き用である.同様に、Java言語のjava.ioパケットには、InputStreamOutputStreamで明確に区別された入力ストリームクラスと出力ストリームクラスが含まれており、両方とも抽象クラスであり、必要に応じてそれぞれのサブクラスを使用してインスタンス化しなければならないことを意味する.
ストリーミング操作によるシンプルなファイルコピー
実際のコードに基づいてstreamを理解するのに役立ちます.以下はC言語標準ライブラリで実現される最も簡単なファイルコピー機能です.
学習の目的のため、このコードは怠けて間違いを許容する機能がなくて、典型的な反面の教材で、しかしwhatever、あなたが本当に持って行ってコンパイルすることを信じないで、本当にファイルを完全にコピーすることができます!ディレクトリをコピーできないほか、存在しないファイルをコピーできない、ファイル権限をコピーできない、目的のファイル名やパスを漏らすことができない、ファイルソフトリンクハードリンクを柔軟に処理できない.待ってblahblah(だから実は简単に见えるcpプログラムさえも大きな要素を考えて支持しなければなりません(ああ、问题を走りました.
// mini_cp.c
#include 
#define BUFFER_SIZE 512

int main(int argc, char *argv[])
{ 
  //           SOURCE   DES    
  FILE *src = fopen(argv[1], "rb");
  FILE *des = fopen(argv[2], "wb");
  
  long int num;
  
  // buffer         
  char buffer[BUFFER_SIZE];

  while(!feof(src)) {
    num = fread(buffer, sizeof(char), BUFFER_SIZE, src);
    fwrite(buffer, sizeof(char), num, des);
  }

  fclose(src);
  fclose(des);

  return 0;
}

この自家製のmini_cpプログラムは理解に難くなく、コアの論理は3つのステップに分解することができる.
  • ソースファイルストリームFILE *srcおよび宛先ファイルストリームFILE *des
  • を開く.
  • サイクル実行{srcストリーム毎に最大512バイトのデータを読み出す=>desストリームを書き込み}ソースファイル読み出しが終了する
  • まで
  • ファイルフロー
  • を閉じる
    コアロジックは非常に明確で、このようなロジックもストリーム操作の普遍的な原理であり、他の言語の実現を試みているが、実際にはすでに大同小異であり、 の概念(または対象)が少なくないことが多い.
    Javaバージョンの同等の実装を見てみましょう.
    import java.io.File;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class Copy {
        
        private static final int BUFFER_SIZE = 512;
    
        public static void main(String[] args) throws IOException {
            
            File srcFile = new File(args[0]);
            File desFile = new File(args[1]);
    
            int recvBytesSize;
            byte[] buffer = new byte[BUFFER_SIZE];
    
            FileInputStream in = new FileInputStream(srcFile);
            FileOutputStream out = new FileOutputStream(desFile);
    
            while((recvBytesSize = in.read(buffer)) != -1) {
                out.write(buffer, 0, recvBytesSize);
            }
    
            in.close();
            out.close();
    
        }
    }

    オブジェクト向けの味が濃くなった(コードが冗長になった)木有?でも、オブジェクト向けだからこそJavaは理論上のstreamをクラスとして抽象化し、クラスのインスタンス(すなわちオブジェクト)を直接取得してオブジェクトを操作しましょう.やはり悪くないでしょう.コードが長くなったのは間違いありませんが、もっとOOですね~
    ここまで書くと、ストリームが基本的にどうなっているのかを答えることができます.では、最後にコピープログラムのruby実装を追加します.
    require 'fileutils'
    FileUtils.cp('SOURCE.txt', 'DEST.txt')
  • ハ?
  • ええ.
  • ...That's why we love Ruby...(逃げる...