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

2023-12-19

我正在尝试读取一个 zip 文件,检查它是否包含一些必需的文件,然后将所有有效文件写入另一个 zip 文件。这java.util.zip 的基本介绍 http://java.sun.com/developer/technicalArticles/Programming/compression/有很多 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。


我不认为使用 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 文件。我们可以使用的技巧是continually中的方法collection.immutable.Stream。它的作用是为您执行一个延迟评估循环。然后,您可以获取并过滤结果以终止并处理您想要的内容。当您想要将某些东西作为迭代器但事实并非如此时,这是一种方便使用的模式。 (如果该项目自行更新,您可以使用.iterate in Iterable or Iterator--这通常更好。)这是这种情况的应用程序,使用两次:一次用于获取条目,一次用于读取/写入数据块:

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 文件的末尾有点不稳定。我认为“正确”的方法是等到你得到一个null从...回来getNextEntry。考虑到这一点,我编辑了之前的代码(有一个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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 ZipInputStreams 和 ZipOutpuStreams 时如何避免 Scala 中的可变变量? 的相关文章

  • Haskell scala 互操作性

    我是 Scala 初学者 来自面向对象范式 在了解 Scala 的函数式编程部分时 我被引导到 Haskell 纯函数式编程语言 探索 SO 问题答案 我发现 Java Haskell 具有互操作性 我很想知道 Scala Haskell
  • IntelliJ IDEA 13:新的 Scala SBT 项目尚未生成 src 目录结构

    我按照 Jetbrains 网站上的入门视频设置 IntelliJ IDEA 13 1 Community Edition 以与 Scala 配合使用 Scala 插件 v0 36 431 已安装 当我使用向导创建一个新的 Scala SB
  • Akka Stream Graph 恢复问题

    我创建了一个图表来并行化具有相同输入的两个流 这些流产生 Future Option Entity 如果 flowA 失败 我想返回 Future None 但恢复似乎没有被调用 val graph Flow Input Future Op
  • HashPartitioner 是如何工作的?

    我阅读了文档HashPartitioner http spark apache org docs 1 3 1 api java index html org apache spark HashPartitioner html 不幸的是 除了
  • Spark SQL 失败,因为“常量池已超过 JVM 限制 0xFFFF”

    我在 EMR 4 6 0 Spark 1 6 1 上运行此代码 val sqlContext SQLContext getOrCreate sc val inputRDD sqlContext read json input try inp
  • 从 HList 获取元素

    我尝试了 HList 并按预期进行了以下工作 val hl 1 foo HNil val i Int hl 0 val s String hl 1 但是 我无法让以下代码正常工作 让我们暂时假设对列表进行随机访问是一个聪明的主意 class
  • 对两种类型之间的二元关系进行建模

    有企业 也有人 用户可以对某个企业点赞或发表评论 但效果是一样的can not发生在一个人身上 当用户发布有关某个企业的内容或对其点赞时 该企业就被称为target喜欢或帖子 trait TargetingRelation Targetin
  • 规范化且不可变的数据模型

    Haskell如何解决 规范化不可变数据结构 问题 例如 让我们考虑一个表示前女友 男友的数据结构 data Man Man name String exes Woman data Woman Woman name String exes
  • Play框架:单属性案例类的JSON读取

    我正在尝试为包含单个属性的案例类创建隐式 JSON Reads 但收到错误 Reads Nothing 不符合预期类型 这是代码 import play api libs functional syntax import play api
  • Scala:什么是 CompactBuffer?

    我试图弄清楚 CompactBuffer 的含义 和迭代器一样吗 请解释其中的差异 根据 Spark 的文档 它是 ArrayBuffer 的替代方案 可以提供更好的性能 因为它分配的内存更少 以下是 CompactBuffer 类文档的摘
  • 理解 Scala FP 库

    只是为了让那些想要开始使用 Scala FP 库 在纯 FP 方面变得更好的人快速清晰地了解 有人能澄清猫和猫效应 猫效应 IO 之间的区别 关系吗 最重要的是 齐奥和莫尼克斯对此有何看法 最后 与 ScalaZ 7 8 有何关系 到目前为
  • andThen 类型不匹配的 Scala 链接函数

    我有一堆函数可以清理文本并将它们分成单词 最小的例子 val txt Mary had a little nlamb val stopwords Seq a def clean text String String text replace
  • 在 Scala 中将元素追加到列表末尾

    我无法添加 type 元素T到一个列表中List T 我尝试过myList myElement但它似乎创建了一个奇怪的对象并访问myList last始终返回放入列表中的第一个元素 我怎么解决这个问题 List 1 2 3 4 Result
  • scala中的反引号有什么用[重复]

    这个问题在这里已经有答案了 我在一本书上找到了以下代码 val list List 5 4 3 2 1 val result 0 list running total next element running total next elem
  • 在scala 2.13中,为什么有时无法显式调用类型类?

    这是 Shapeless 2 3 3 中的一个简单示例 val book author gt gt Benjamin Pierce title gt gt Types and Programming Languages id gt gt 2
  • Erlang:如何将原子转换为字符串?

    我想从原子转换为字符串 Input hello world Output hello world 我该如何实现这一目标 Use atom to list http erlang org doc man erlang html atom to
  • 如何执行仅匹配正则表达式的测试?

    在 sbt 0 10 1 中 我经常使用test only缩小我的测试数量 sbt gt test only com example MySpec 但是 我想缩小范围 以便只运行名称 描述与正则表达式匹配的测试 是否有一些语法可以实现这样的
  • Spark 2.2 无法将 df 写入 parquet

    我正在构建一个聚类算法 我需要存储模型以供将来加载 我有一个具有以下架构的数据框 val schema new StructType add StructField uniqueId LongType add StructField tim
  • 正确使用术语 Monoid

    从下面的例子来看 我认为这样的说法是正确的String在串联运算下定义了一个幺半群 因为它是关联二元运算 并且String碰巧有一个身份元素 它是一个空字符串 scala gt Jane Doe Jane Doe res0 Boolean
  • 如何关闭 Scala 中因方法重载而导致代码无法编译的特定隐式?

    我正忙着尝试自己回答这个问题 Scala Play 2 4 x 通过 anorm MySQL 处理扩展字符到 Java Mail https stackoverflow com questions 31417718 scala play 2

随机推荐