【问题标题】:Reading a large file in functional scala在函数式scala中读取一个大文件
【发布时间】:2011-10-14 21:24:17
【问题描述】:

我正在尝试使用 scala 处理一个大型二进制文件。如果可能的话,我想使用功能方法。我的主要方法现在看起来像这样:

def getFromBis( buffer:List[Byte], bis:BufferedInputStream ):(Byte,List[Byte],Boolean) = {
    buffer match {
        case Nil =>
            val buffer2 = new Array[Byte](100000)
            bis.read(buffer2) match {
                case -1 => (-1,Nil,false)
                case _  => 
                    val buffer3 = buffer2.toList
                    (buffer3.head,buffer3.tail,true)
            }
        case b::tail => return (b,tail,true)
    }
}

它需要一个列表缓冲区和一个缓冲的输入流。如果缓冲区不为空,则仅返回头部和尾部,如果为空,则从文件中获取下一个块并将其用作缓冲区。

如您所见,这不是很实用。我试图以一种尽可能少的潜在 io 调用的方式来执行此操作,这就是为什么我以分块方式执行此操作的原因。这里的问题是新的数组。每次我运行该函数时,它都会创建一个新数组,并且根据程序运行时不断增加的内存使用量来判断,我认为它们不会被破坏。

我的问题是:有没有更好的方法来使用 scala 以分块方式读取大文件?我想保留一个完全函数式的方法,但至少我需要一个函数,它可以充当我函数式程序其余部分的黑匣子。

【问题讨论】:

  • This post 关于以类似事件流的方式处理大文件可以帮助您。很旧了,但我觉得还是不错的。

标签: java scala file-io


【解决方案1】:

您几乎肯定不想将字节存储在List 中。每个字节都需要一个新对象。这确实效率低下,并且可能会导致您需要的内存使用量增加 20 倍。

最简单的方法是创建一个存储内部状态的迭代器:

class BisReader(bis: BufferedInputStream) {
  val buffer = new Array[Byte](100000)
  var n = 0
  var i = 0
  def hasNext: Boolean = (i < n) || (n >= 0 && {
    n = bis.read(buffer)
    i = 0
    hasNext
  })
  def next: Byte = {
    if (i < n) {
      val b = buffer(i)
      i += 1
      b
    }
    else if (hasNext) next
    else throw new IOException("Input stream empty")
  }
}
implicit def reader_as_iterator(br: BisReader) = new Iterator[Byte] {
  def hasNext = br.hasNext
  def next = br.next
}

可以让 BisReader 扩展 Iterator[Byte],但由于 Iterator 不是专用的,这需要对原始 next/hasNext 访问进行装箱。这样,您可以在需要时全速获得低级 (next/hasNext) 访问,否则使用方便的迭代器方法。

现在您已经将丑陋的非功能性 Java IO 东西隔离在一个具有干净接口的类中,并且可以恢复为功能性。


编辑:当然,除了 IO 是依赖于顺序的并且有副作用,但是之前的方法也不能解决这个问题。

【讨论】:

  • 你太棒了,谢谢!到目前为止,我一直在做的只是发送那个字节列表,到处走动,但我想我可以发送一个索引变量。
  • @Mediocre Gopher - 只需发送BisReader 的实例,并在需要时获取带有next 的项目。如果您想要最大效率,您也可以创建自己的 getOrElse,这样您就不必担心一直检查 hasNext
  • 你不是还在对每个字节进行装箱,还是迭代器得到了专门化处理?
  • @oxbow_lakes - 如果您使用 Iterator[Byte],它会被装箱。如果您使用BisReader,它只是看起来 像一个迭代器,但使用的是原语。如果需要,隐式包装器将回到拳击,但至少你有一个高性能的hasNextnext
  • @Rex - 你是对的;当我在答案中阅读“迭代器”时,我只是假设您的BisReader 实现了Iterator
猜你喜欢
  • 1970-01-01
  • 2021-02-11
  • 2010-11-20
  • 2014-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-13
  • 2014-03-03
相关资源
最近更新 更多