当使用ZipInputStreams和ZipOutpuStreams时,如何避免Scala中的可
我试图读取一个zip文件,检查它是否有一些必需的文件,然后将所有有效的文件写入另一个zip文件。
basic introduction to java.util.zip有很多Java主题,我很想让我的代码更加Scala-native。具体来说,我想避免使用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,1024) while (count != -1) { zipOut.write(data,count) count = zipIn.read(data,1024) } } zipIn.close } zipOut.close 我应该补充说,我正在使用Scala 2.7.7。 解决方法
我不认为使用被设计为以设计的方式以强制性方式工作的Java类有什么特别的错误。惯用的Scala包括能够使用惯用的Java,即使风格有冲突。
但是,如果你想要 – 也许是一个练习,或者也许是因为它稍微澄清逻辑 – 以更有功能的无变量方式来做到这一点,你可以这么做。在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文件。我们可以使用的技巧是在collection.immutable.Stream中不断的方法。它所做的是为您执行一个延迟评估的循环。然后,您可以对结果进行过滤并终止并处理所需的结果。当你有一些想要成为迭代器的东西时,它是一种方便的模式,但不是。 (如果项目更新本身,可以在Iterable或Iterator中使用.iterate,这通常更好)。这里是这种情况下的应用程序,使用两次:一次获取条目,一次读取/写入数据块: 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,count)) }) } zipIn.close zipOut.close 密切关注。在一些线的尽头!我通常会在一行中写下这个,但是它更好的包装,所以你可以看到这一切。 为了防止这种情况不清楚,我们来解释一个不断的用途。 Stream.continually(zipIn.read(buffer)) 这要求根据需要继续调用zipIn.read(缓冲区)多次,存储结果的整数。 .takeWhile(_ != -1) 这指定需要多少次,返回一个无限长的流,但是当它达到-1时将退出。 .foreach(count => zipOut.write(buffer,count)) 这将处理流,依次使用每个项目(计数),并使用它来编写缓冲区。这样做的工作方式有点笨拙,因为你依靠zipIn刚刚被称为获取流的下一个元素 – 如果你再次尝试这样做,而不是单次通过流,那将会失败因为缓冲区将被覆盖。但这里没关系 所以,它是:稍微更紧凑,可能更容易理解,可能不太容易理解的方法更具功能(尽管仍然存在副作用)。在2.7.7中,相比之下,我实际上会使用Java的方式,因为Stream.continually不可用,并且构建自定义迭代器的开销对于这种情况是不值得的。 (这将是值得的,如果我要做更多的zip文件处理,然后可以重用代码。) 编辑:查找可用到零的方法是检测zip文件的结尾的一种薄片。我认为“正确”的方法是等待,直到你从getNextEntry返回null。考虑到这一点,我已经编辑了以前的代码(现在有一个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,cb._1)) }) zipIn.close zipOut.close (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |