scala quirky在这个while循环代码中

sma*_*007 7 scala

昨天,这段代码让我头疼.我通过逐行读取文件来修复它.有任何想法吗 ?

即使文件中的行数大于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
 }
Run Code Online (Sandbox Code Playgroud)

0__*_*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
Run Code Online (Sandbox Code Playgroud)

你看,当你执行size两次时,结果为零.

有两种解决方案,要么将迭代器强制为"稳定",就像lines.toSeq.或者你忘了size并进行"正常"迭代:

while(lines.hasNext) {
  val linesSub = lines.take(10)
  println("batch:" + linesSub.size)
  // do something with linesSub
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为`Iterator`不应该有`size`方法.如果你提供一个,它不应该有副作用,因为大多数理智的人不会期望它们.充其量,方法名称应该不同.也许`count()`? (4认同)

Kev*_*ght 5

以上所有答案都没有达到头上的钉子.

Iterator是一个很好的理由回到这里.通过延迟,它会从堆中获取压力,然后表示每行的String可以在完成后立即进行垃圾回收.对于大文件,这可以避免OutOfMemoryException的所有区别.

理想情况下,您可以直接使用迭代器,而不是强制它进入严格的集合类型.

使用grouped的话,按OM-NOM-NOM的回答:

for (linesSub <- lines grouped 10) {
  //do something with linesSub
}
Run Code Online (Sandbox Code Playgroud)

如果你想保留println计数器,请在索引中压缩:

for ( (linesSub, batchIdx) <- (lines grouped 10).zipWithIndex ) {
  println("batch " + batchIdx)
  //do something with linesSub
}
Run Code Online (Sandbox Code Playgroud)

如果你真的需要总数,请调用getLines两次.一次计数,第二次实际处理线.