D No creo que haya nada malo en particular con el uso de las clases de Java que están diseñados para trabajar de manera imprescindible de la manera que fueron diseñados. Idiomatic Scala incluye poder usar Java idiomático como estaba previsto, incluso si los estilos chocan un poco.
Sin embargo, si lo desea, quizás como ejercicio, o tal vez porque aclara un poco la lógica, para hacerlo de una manera más funcional y sin var, puede hacerlo. En 2.8, es particularmente agradable, así que aunque estés usando 2.7.7, daré una respuesta de 2.8.
En primer lugar, tenemos que plantear el problema, que no lo hizo del todo, pero vamos a suponer que tenemos algo como esto:
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
Ahora, teniendo en cuenta esto queremos copiar el archivo zip. El truco que podemos usar es el método continually
en collection.immutable.Stream
. Lo que hace es realizar un bucle evaluado de forma lenta para ti. A continuación, puede tomar y filtrar los resultados para finalizar y procesar lo que desea. Es un patrón útil para usar cuando tiene algo que desea que sea un iterador, pero no lo es. (Si el artículo se actualiza solo puede usar .iterate
en Iterable
o Iterator
--que suele ser incluso mejor.) Aquí está la aplicación a este caso, utilizada dos veces: una para obtener las entradas y una para leer/escribir fragmentos de datos:
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
Preste mucha atención al .
al final de algunas líneas! Normalmente escribiría esto en una línea larga, pero es más agradable tenerlo envuelto para que pueda verlo todo aquí.
Justo en caso de que no esté claro, desagilemos uno de los usos de continually
.
Stream.continually(zipIn.read(buffer))
Esta pregunta que seguir llamando zipIn.read(buffer)
para tantas veces como sea necesario, almacenar el entero resultante.
.takeWhile(_ != -1)
Esto especifica cuántas veces son necesarias, volviendo una corriente de longitud indefinida, sino que será dejar de fumar cuando se realiza un -1
.
.foreach(count => zipOut.write(buffer,0,count))
Este procesa el flujo, teniendo cada elemento a su vez (la cuenta), y usarlo para escribir la memoria intermedia.Esto funciona de una manera un poco furtiva, ya que se basa en el hecho de que se ha llamado al zipIn
para obtener el siguiente elemento de la transmisión: si intentaba hacer esto de nuevo, no en un solo paso a través de la secuencia, fallaría porque buffer
se sobrescribirá. Pero aquí está bien.
Por lo tanto, ahí está: un método un poco más compacto, posiblemente más fácil de entender, posiblemente menos fácil de entender que sea más funcional (aunque todavía hay abundancia de efectos secundarios). En 2.7.7, en cambio, realmente lo haría de la manera Java porque Stream.continually
no está disponible, y la sobrecarga de construir un Iterator
personalizado no vale la pena en este caso. (Sería la pena si iba a hacer más procesamiento de archivos zip y podría reutilizar el código, sin embargo.)
Editar: El mirar-para-disponibles-a-go-cero método es una especie de escamosa para detectar el final del archivo comprimido. Creo que la forma "correcta" es esperar hasta que obtenga null
de vuelta desde getNextEntry
. Con eso en mente, he editado el código anterior (había un takeWhile(_ => zipIn.available==1)
que ahora es un takeWhile(_ != null)
) y proporcioné una versión basada en el iterador 2.7.7 a continuación (observe qué tan pequeño es el bucle principal, una vez que termina el trabajo de definir los iteradores, que sí es cierto utilizan 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
¿Por qué es nula de datos? – sblundy
Porque estaba siendo flojo y 'new Array [Byte]' hace que el compilador se queje sobre constructores alternativos. Supongo que debería usar 'new ArrayBuffer [Byte]'. – pr1001
var data = new Array [Byte] (1024) –