dI glaube nicht, dass es etwas besonders falsch mit Java-Klassen, die in imperativen Art und Weise zu arbeiten, in der Art und Weise gestaltet sind sie entworfen. Idiomatische Scala beinhaltet die Fähigkeit, idiomatisches Java so zu verwenden, wie es beabsichtigt war, auch wenn die Stile ein wenig kollidieren.
Wenn Sie jedoch möchten - vielleicht als Übung, oder vielleicht, weil es die Logik ein wenig verdeutlicht - um dies auf eine funktionellere var-freie Weise zu tun, können Sie dies tun. In 2.8 ist es besonders nett, also, obwohl Sie 2.7.7 verwenden, gebe ich eine 2.8 Antwort.
Zuerst müssen wir das Problem einzurichten, die Sie nicht ganz getan hat, aber nehmen wir an, wir etwas davon haben:
import java.io._
import java.util.zip._
import scala.collection.immutable.Stream
val fos = new FileOutputStream("new.zip")
val zipOut = new ZipOutputStream(new BufferedOutputStream(fos))
val zipIn = new ZipInputStream(new FileInputStream("old.zip"))
def entryIsValid(ze: ZipEntry) = !ze.isDirectory
Jetzt, da das wir die Zip-Datei kopieren möchten. Der Trick, den wir verwenden können, ist die continually
Methode in collection.immutable.Stream
. Was es tut, ist eine träge bewertete Schleife für Sie durchzuführen. Sie können dann die Ergebnisse aufnehmen und filtern, um das zu beenden und zu verarbeiten, was Sie wollen. Es ist ein praktisches Muster, wenn Sie etwas haben, das Sie als Iterator verwenden möchten, aber nicht. (Wenn die Artikel-Updates selbst Sie .iterate
in Iterable
oder Iterator
können --that in der Regel sogar besser.) Hier ist die Anwendung auf diesen Fall, zweimal verwendet: einmal um die Einträge zu erhalten, und einmal/Schreib-Datenblöcke zu lesen:
val buffer = new Array[Byte](1024)
Stream.continually(zipIn.getNextEntry).
takeWhile(_ != null).filter(entryIsValid).
foreach(entry => {
zipOut.putNextEntry(new ZipEntry("subdir/"+entry.getName))
Stream.continually(zipIn.read(buffer)).takeWhile(_ != -1).
foreach(count => zipOut.write(buffer,0,count))
})
}
zipIn.close
zipOut.close
Achten Sie auf die .
am Ende einiger Zeilen! Ich würde das normalerweise auf eine lange Zeile schreiben, aber es ist schöner, es zu wickeln, damit man alles hier sehen kann.
Nur für den Fall, dass es nicht klar ist, lassen Sie uns eine der Anwendungen von continually
entpacken.
Stream.continually(zipIn.read(buffer))
Das fragt zipIn.read(buffer)
für so oft wie nötig, Speichern der integer zu halten Aufruf, die Ergebnisse.
.takeWhile(_ != -1)
Diese gibt an, wie oft notwendig sind, um einen Strom von unbestimmter Länge zurückkehrt, aber das wird beendet, wenn er ein -1
trifft.
Dies verarbeitet den Stream und nimmt jedes Element der Reihe nach (die Zählung) und verwendet es, um den Puffer zu schreiben.Dies funktioniert auf eine leicht hinterhältige Art und Weise, da Sie sich darauf verlassen, dass zipIn
gerade aufgerufen wurde, um das nächste Element des Streams zu erhalten - wenn Sie dies erneut versuchen, nicht bei einem einzigen Durchlauf durch den Stream, würde es fehlschlagen weil buffer
würde überschrieben werden. Aber hier ist es in Ordnung.
Also, es ist: eine etwas kompaktere, möglicherweise einfacher zu verstehen, möglicherweise weniger leicht zu verstehen, Methode, die funktionaler ist (obwohl es immer noch Nebenwirkungen in Hülle und Fülle). In 2.7.7 dagegen würde ich es tatsächlich auf Java-Art machen, weil Stream.continually
nicht verfügbar ist, und der Aufwand für die Erstellung einer benutzerdefinierten Iterator
ist es für diesen einen Fall nicht wert. (Es wäre wert, wenn ich will mehr Zip-Datei Verarbeitung tun und den Code wiederverwenden kann, jedoch.)
Edit: Die Suche-for-Available-to-go-Null-Methode ist eine Art flockig, um das Ende der Zip-Datei zu erkennen. Ich denke, der "korrekte" Weg ist zu warten, bis Sie eine null
zurück von bekommen. In diesem Sinne habe ich den vorherigen Code bearbeitet (es gab eine takeWhile(_ => zipIn.available==1)
, die jetzt eine takeWhile(_ != null)
ist) und eine 2.7.7 Iterator basierte Version unten zur Verfügung gestellt (beachten Sie, wie klein die Hauptschleife ist, sobald Sie durch die Definition arbeiten die Iteratoren, die zwar nicht verwenden, vARs):
val buffer = new Array[Byte](1024)
class ZipIter(zis: ZipInputStream) extends Iterator[ZipEntry] {
private var entry:ZipEntry = zis.getNextEntry
private var cached = true
private def cache { if (entry != null && !cached) {
cached = true; entry = zis.getNextEntry
}}
def hasNext = { cache; entry != null }
def next = {
if (!cached) cache
cached = false
entry
}
}
class DataIter(is: InputStream, ab: Array[Byte]) extends Iterator[(Int,Array[Byte])] {
private var count = 0
private var waiting = false
def hasNext = {
if (!waiting && count != -1) { count = is.read(ab); waiting=true }
count != -1
}
def next = { waiting=false; (count,ab) }
}
(new ZipIter(zipIn)).filter(entryIsValid).foreach(entry => {
zipOut.putNextEntry(new ZipEntry("subdir/"+entry.getName))
(new DataIter(zipIn,buffer)).foreach(cb => zipOut.write(cb._2,0,cb._1))
})
zipIn.close
zipOut.close
Warum ist Daten null? – sblundy
Weil ich faul war und 'neues Array [Byte]' den Compiler dazu bringt, sich über alternative Konstruktoren zu beschweren. Ich denke, ich sollte 'new ArrayBuffer [Byte]' verwenden. – pr1001
var data = new Array [Byte] (1024) –