昨天,这段代码让我头疼。我通过逐行读取文件来修复它。有什么想法吗?
即使文件中的行数不大于1,while循环也似乎永远不会被执行。
val lines = Source.fromFile( new File("file.txt") ).getLines;
println( "total lines:"+lines.size );
var starti = 1;
while( starti < lines.size ){
val nexti = Math.min( starti + 10, lines.size );
println( "batch ("+starti+", "+nexti+") total:" + lines.size )
val linesSub = lines.slice(starti, nexti)
//do something with linesSub
starti = nexti
}
答案 0 :(得分:14)
这确实很棘手,我甚至会说它是Iterator
中的一个错误。 getLines
会返回Iterator
懒惰的内容。所以似乎发生的事情是,如果你要求lines.size
,迭代器会遍历整个文件来计算行数。之后,它“筋疲力尽”:
scala> val lines = io.Source.fromFile(new java.io.File("....txt")).getLines
lines: Iterator[String] = non-empty iterator
scala> lines.size
res4: Int = 15
scala> lines.size
res5: Int = 0
scala> lines.hasNext
res6: Boolean = false
您会看到,当您执行size
两次时,结果为零。
有两种解决方案,要么将迭代器强制为“稳定”,要么lines.toSeq
。或者你忘了size
并进行“正常”迭代:
while(lines.hasNext) {
val linesSub = lines.take(10)
println("batch:" + linesSub.size)
// do something with linesSub
}
答案 1 :(得分:5)
上述答案都没有达到最佳状态。
Theres是在这里返回Iterator
的一个很好的理由。通过延迟,它会从堆中获取压力,然后表示每行的String可以在完成后立即进行垃圾回收。对于大文件,这可以避免OutOfMemoryException。
理想情况下,您可以直接使用迭代器,而不是强制它进入严格的集合类型。
然后使用grouped
,根据om-nom-nom的答案:
for (linesSub <- lines grouped 10) {
//do something with linesSub
}
如果您想保留println
计数器,请在索引中压缩:
for ( (linesSub, batchIdx) <- (lines grouped 10).zipWithIndex ) {
println("batch " + batchIdx)
//do something with linesSub
}
如果确实需要总数,请两次调用getLines
。一次计数,第二次实际处理线。
答案 2 :(得分:4)
第二次调用lines.size时,它返回0.这是因为lines
是迭代器,而不是数组。
答案 3 :(得分:4)
我已经以Seq
方式重写了您的代码,这是在@ 0__回答中提出的:
val batchSize = 10;
val lines = Source.fromFile("file.txt").getLines.toSeq;
println( "total lines:"+lines.length);
var processed = 0;
lines.grouped(batchSize).foreach( batch => {
println( "batch ("+processed+","+(processed+Math.min(lines.length-processed,batchSize))+")
total:"+lines.length
);
processed = processed + batchSize;
//do something with batch
}
)