gpt4 book ai didi

scala - 使用 ZipInputStreams 和 ZipOutpuStreams 时如何避免 Scala 中的可变变量?

转载 作者:行者123 更新时间:2023-12-03 14:34:01 26 4
gpt4 key购买 nike

我正在尝试读取一个 zip 文件,检查它是否有一些必需的文件,然后将所有有效文件写入另一个 zip 文件。 basic introduction to java.util.zip有很多 Java 主义,我很想让我的代码更原生于 Scala。具体来说,我想避免使用 vars .这是我所拥有的:

val fos = new FileOutputStream("new.zip");
val zipOut = new ZipOutputStream(new BufferedOutputStream(fos));

while (zipIn.available == 1) {
val entry = zipIn.getNextEntry
if (entryIsValid(entry)) {
zipOut.putNewEntry(new ZipEntry("subdir/" + entry.getName())
// read data into the data Array
var data = Array[Byte](1024)
var count = zipIn.read(data, 0, 1024)
while (count != -1) {
zipOut.write(data, 0, count)
count = zipIn.read(data, 0, 1024)
}
}
zipIn.close
}
zipOut.close

我应该补充一点,我正在使用 Scala 2.7.7。

最佳答案

d我认为使用 Java 类并没有什么特别的错误,这些 Java 类被设计为按照它们设计的方式以命令式方式工作。惯用的 Scala 包括能够按预期使用惯用的 Java,即使样式确实有点冲突。

然而,如果你想——也许是作为一个练习,或者可能是因为它确实稍微澄清了逻辑——以一种更实用的无 var 的方式来做到这一点,你可以这样做。在 2.8 中,它特别好,所以即使你使用的是 2.7.7,我也会给出 2.8 的答案。

首先,我们需要设置问题,但您并没有完全解决这个问题,但假设我们有这样的事情:

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

现在,鉴于此,我们要复制 zip 文件。我们可以使用的技巧是 continually collection.immutable.Stream 中的方法.它所做的是为您执行一个惰性求值循环。然后,您可以获取并过滤结果以终止和处理您想要的结果。当您想要成为迭代器时使用它是一种方便的模式,但事实并非如此。 (如果项目自行更新,您可以在 .iterateIterable 中使用 Iterator ——这通常会更好。)这是此案例的应用程序,使用了两次:一次用于获取条目,一次用于读/写数据 block :
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

密切关注 .在一些行的末尾!我通常会把它写在一条长线上,但最好把它包起来,这样你就可以在这里看到所有内容。

以防万一,让我们解开 continually 的用途之一。 .
Stream.continually(zipIn.read(buffer))

这要求继续调用 zipIn.read(buffer)根据需要多次存储结果的整数。
.takeWhile(_ != -1)

这指定了需要多少次,返回一个不定长度的流,但是当它到达 -1 时会退出。 .
.foreach(count => zipOut.write(buffer,0,count))

这会处理流,依次获取每个项目(计数),并使用它来写入缓冲区。这有点偷偷摸摸地工作,因为您依赖于 zipIn 的事实。刚刚被调用以获取流的下一个元素——如果您尝试再次执行此操作,而不是单次通过流,它将失败,因为 buffer将被覆盖。但是这里没关系。

所以,它就是:一个稍微更紧凑、可能更容易理解、可能不太容易理解的更实用的方法(尽管仍然有很多副作用)。相比之下,在 2.7.7 中,我实际上会使用 Java 方式,因为 Stream.continually不可用,并且构建自定义的开销 Iterator对于这种情况不值得。 (但是,如果我要进行更多的 zip 文件处理并且可以重用代码,那将是值得的。)

编辑:寻找可用的归零方法对于检测 zip 文件的结尾有点不稳定。我认为“正确”的方法是等到你得到 nullgetNextEntry 返回.考虑到这一点,我编辑了之前的代码(有一个 takeWhile(_ => zipIn.available==1) 现在是 takeWhile(_ != null) )并在下面提供了一个基于 2.7.7 迭代器的版本(注意主循环有多小,一旦你通过定义迭代器的工作,它们确实使用 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

关于scala - 使用 ZipInputStreams 和 ZipOutpuStreams 时如何避免 Scala 中的可变变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2849303/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com