在函数scala中读取大文件

时间:2011-10-14 21:24:18

标签: java scala file-io

我正在尝试使用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)
    }
}

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

正如您所看到的,这不是很实用。我试图以尽可能少的潜在io调用的方式这样做,这就是为什么我以一种分块的方式做这件事。这里的问题是新的数组。每次我运行该函数时它都会创建一个新的数组,并且在程序运行时不断增加的内存使用情况来判断,我认为它们不会被破坏。

我的问题是:有没有更好的方法来使用scala以分块方式阅读大文件?我想保持一个完全功能性的方法,但至少我需要一个函数,它可以作为我的功能程序的其余部分的黑盒子。

1 个答案:

答案 0 :(得分:7)

您几乎肯定不希望在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依赖于顺序并且有副作用,但前面的方法也没有解决这个问题。