我正在尝试使用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以分块方式阅读大文件?我想保持一个完全功能性的方法,但至少我需要一个函数,它可以作为我的功能程序的其余部分的黑盒子。
答案 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依赖于顺序并且有副作用,但前面的方法也没有解决这个问题。