Wie Tagir Valeev erwähnt, diese Art von Problem wird von der Stream-API nicht gut unterstützt. Wenn Sie inkrementell Zeilen aus der Eingabe lesen und übereinstimmende Zeilen mit Kontext drucken möchten, müssen Sie eine Stateful-Pipelinestufe (oder einen benutzerdefinierten Collector oder Spliterator) einführen, was ein wenig Komplexität mit sich bringt.
Wenn Sie bereit sind, alle Zeilen in den Speicher zu lesen, stellt sich heraus, dass BitSet
eine nützliche Darstellung für das Manipulieren von Übereinstimmungsgruppen ist. Dies hat eine gewisse Ähnlichkeit mit Tagirs Lösung, aber anstatt Ganzzahlbereiche zu verwenden, um zu druckende Zeilen darzustellen, verwendet es 1-Bits in einer BitSet
. Einige Vorteile von BitSet
sind, dass es eine Reihe von integrierten Massenoperationen hat, und es hat eine kompakte interne Darstellung. Es kann auch einen Strom von Indizes der 1-Bits erzeugen, was für dieses Problem ziemlich nützlich ist.
Zuerst lassen Sie uns beginnen, indem sie einen BitSet
schaffen, die ein 1-Bit für jede Zeile hat, die das Prädikat übereinstimmt:
void contextMatch(Predicate<String> pred, int before, int after, List<String> input) {
int len = input.size();
BitSet matches = IntStream.range(0, len)
.filter(i -> pred.test(input.get(i)))
.collect(BitSet::new, BitSet::set, BitSet::or);
Nun, da wir das Bit Reihe von passenden Linien haben wir die Indizes ausströmen von jedem 1-Bit. Dann setzen wir die Bits im Bitset, die den Vorher-Nachher-Kontext darstellen. Dies gibt uns eine einzige BitSet
, deren 1-Bit alle Zeilen einschließlich der Kontextlinien darstellen.
BitSet context = matches.stream()
.collect(BitSet::new,
(bs,i) -> bs.set(Math.max(0, i - before), Math.min(i + after + 1, len)),
BitSet::or);
Wenn wir nur die Linien alle drucken möchten, einschließlich Kontext, können wir dies tun:
context.stream()
.forEachOrdered(i -> System.out.println(input.get(i)));
Die tatsächlichen grep -A a -B b
Befehl druckt eine Trenneinrichtung zwischen jeder Gruppe von Kontextzeilen. Um herauszufinden, wann ein Trennzeichen gedruckt werden soll, betrachten wir jedes 1-Bit im Kontext-Bit-Set. Wenn ein 0-Bit davor steht, oder wenn es ganz am Anfang steht, setzen wir ein bisschen in das Ergebnis. Dies gibt uns einen 1-Bit am Anfang jeder Gruppe von Kontextzeilen:
Wir wollen nicht vor jeder Gruppe von Kontextzeilen des Separator drucken; wir wollen es zwischen jeder Gruppe drucken.Das heißt, wir müssen das erste 1-Bit löschen (falls vorhanden):
// clear the first bit
int first = separators.nextSetBit(0);
if (first >= 0) {
separators.clear(first);
}
Jetzt können wir die Ergebniszeilen ausdrucken. Aber vor jeder Zeile gedruckt wird, überprüfen wir, ob wir einen Separator zuerst gedruckt werden soll:
context.stream()
.forEachOrdered(i -> {
if (separators.get(i)) {
System.out.println("--");
}
System.out.println(input.get(i));
});
}
, die leider ist nicht von dem Stream-API aus der Box unterstützt, aber was Sie wollen, ist ein „Schiebefenster“ genannt. –