在函数scala中读取大文件

Med*_*her 4 java file-io scala

我正在尝试使用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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

Rex*_*err 7

你几乎肯定不想在a中存储字节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
}
Run Code Online (Sandbox Code Playgroud)

可能有BisReader扩展Iterator [Byte],但由于Iterator不是专门的,这将需要装箱原始next/hasNext访问.这样,您可以在需要时以全速访问低级别(next/hasNext),否则使用方便的迭代器方法.

现在,您已经将具有干净界面的丑陋的非功能性Java IO内容隔离在一个类中,并且可以恢复正常运行.


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