web-dev-qa-db-ja.com

複数の異なるInputStreamを1つのInputStreamにチェーンする方法

Java(またはScala)。

必要なのは、FTPサーバーからネットワーク経由で読み込むフラットファイルを解析することです。私がやりたいのは、file [1..N]を取得し、ストリームを開き、それらを1つのストリームに結合することです。したがって、file1が終了したら、fileNの最後に到達するまで、file2からの読み取りを開始します。

これらのファイルを特定の順序で読み取る必要があり、データはバーチでファイルを生成するレガシーシステムから取得されるため、1つのデータは別のファイルのデータに依存しますが、ドメインロジックインターフェイスを簡素化するために1つの連続したストリームとして処理したいと思います。

私は周りを検索してPipedInputStreamを見つけましたが、私はそれが必要なことではありません。例が役立ちます。

57
Magnus

JDKにはまさにそこにあります!引用 SequenceInputStreamのJavaDoc

SequenceInputStreamは、他の入力ストリームの論理的な連結を表します。入力ストリームの順序付けられたコレクションで始まり、ファイルの終わりに達するまで最初のストリームから読み取り、その後、含まれる入力ストリームの最後でファイルの終わりに達するまで、2番目のストリームから読み取ります。

任意の数のInputStreamsを連結する一方で、SequenceInputStreamは2つだけを受け入れます。ただし、SequenceInputStreamInputStreamでもあるため、再帰的に適用(ネスト)できます。

new SequenceInputStream(
    new SequenceInputStream(
        new SequenceInputStream(file1, file2),
        file3
    ),
    file4
);

...あなたはアイデアを得る。

こちらもご覧ください

94

これは、Tomasz Nurkiewiczの答えが示すように、Javaでは簡単なSequencedInputStreamを使用して行われます。最近プロジェクトでこれを繰り返し行わなければならなかったので、「pimp my library」パターンを介してScala-yの良さを追加しました。

object StreamUtils {
  implicit def toRichInputStream(str: InputStream) = new RichInputStream(str)

  class RichInputStream(str: InputStream) {
// a bunch of other handy Stream functionality, deleted

    def ++(str2: InputStream): InputStream = new SequenceInputStream(str, str2)
  }
}

それにより、次のようにストリームシーケンスを実行できます。

val mergedStream = stream1++stream2++stream3

あるいは

val streamList = //some arbitrary-length list of streams, non-empty
val mergedStream = streamList.reduceLeft(_++_)
15
Dave Griffith

別の解決策:最初に入力ストリームのリストを作成してから、入力ストリームのシーケンスを作成します。

List<InputStream> iss = Files.list(Paths.get("/your/path"))
        .filter(Files::isRegularFile)
        .map(f -> {
            try {
                return new FileInputStream(f.toString());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).collect(Collectors.toList());

new SequenceInputStream(Collections.enumeration(iss)))
11
freedev

Vectorを使用したよりエレガントなソリューションを以下に示します。これはAndroid

    AssetManager am = getAssets();
    Vector v = new Vector(Constant.PAGES);
    for (int i =  0; i < Constant.PAGES; i++) {
        String fileName = "file" + i + ".txt";
         InputStream is = am.open(fileName);
         v.add(is);
    }
    Enumeration e = v.elements();
    SequenceInputStream sis = new SequenceInputStream(e);
    InputStreamReader isr = new InputStreamReader(sis);

    Scanner scanner = new Scanner(isr);   // or use bufferedReader
1
Ryan Heitner

簡単なScala Iterator[InputStream]を連結するバージョンです:

import Java.io.{InputStream, SequenceInputStream}
import scala.collection.JavaConverters._

def concatInputStreams(streams: Iterator[InputStream]): InputStream =
  new SequenceInputStream(streams.asJavaEnumeration)
0
Carl Ekerot