Ich frage mich, ob es eine ideomatische Möglichkeit gibt, mehrere InputStreams in einem kontinuierlichen InputStream in Java (oder Scala) zu verketten.
Dafür brauche ich flache Dateien, die ich über das Netzwerk von einem FTP-Server aus lade. Ich möchte die Datei [1..N] nehmen, Streams öffnen und diese dann zu einem Stream zusammenfassen. Wenn file1 dann zu Ende geht, möchte ich aus file2 und so weiter lesen, bis ich das Ende von fileN erreiche.
Ich muss diese Dateien in einer bestimmten Reihenfolge lesen. Die Daten stammen aus einem Altsystem, das Dateien in Flanken erzeugt. Die Daten in einer Datei hängen von den Daten in einer anderen Datei ab. Ich möchte sie jedoch als einen kontinuierlichen Stream behandeln, um meine Domänenlogikschnittstelle zu vereinfachen .
Ich habe herumgesucht und PipedInputStream gefunden, aber ich bin nicht sicher, dass es das ist, was ich brauche. Ein Beispiel wäre hilfreich.
Es ist genau dort in JDK! Zitieren JavaDoc von SequenceInputStream
:
Eine
SequenceInputStream
repräsentiert die logische Verkettung anderer Eingabeströme. Es beginnt mit einer geordneten Sammlung von Eingabeströmen und Lesevorgängen vom ersten bis zum Ende der Datei, woraufhin es vom zweiten liest, usw., bis das Ende der Datei auf dem letzten der enthaltenen Eingabeströme erreicht ist.
Sie möchten eine beliebige Anzahl von InputStream
s verketten, während SequenceInputStream
nur zwei akzeptiert. Da SequenceInputStream
auch eine InputStream
ist, können Sie sie rekursiv anwenden (verschachteln):
new SequenceInputStream(
new SequenceInputStream(
new SequenceInputStream(file1, file2),
file3
),
file4
);
...Du hast die Idee.
Dies geschieht mit SequencedInputStream, was in Java unkompliziert ist, wie die Antwort von Tomasz Nurkiewicz zeigt. Ich musste dies kürzlich wiederholt in einem Projekt durchführen, also habe ich Scala-y-Güte über das Muster "pimp my library" hinzugefügt.
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)
}
}
Damit kann ich die Stream-Sequenzierung wie folgt durchführen
val mergedStream = stream1++stream2++stream3
oder auch
val streamList = //some arbitrary-length list of streams, non-empty
val mergedStream = streamList.reduceLeft(_++_)
Eine andere Lösung: Erstellen Sie zuerst eine Liste der Eingabeströme und dann die Reihenfolge der Eingabeströme:
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)))
Hier ist eine elegantere Lösung unter Verwendung von Vector, dies ist für Android spezifisch, aber Vector für jedes Java
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
Hier ist eine einfache Scala-Version, die einen Iterator[InputStream]
verkettet:
import Java.io.{InputStream, SequenceInputStream}
import scala.collection.JavaConverters._
def concatInputStreams(streams: Iterator[InputStream]): InputStream =
new SequenceInputStream(streams.asJavaEnumeration)