如何通过Scala中的自定义函数对大文本文件中的行进行排序?

时间:2015-11-17 12:00:06

标签: arrays scala sorting

我的文本文件的结构是第一行包含制表符分隔键,所有后续行都包含这些键的制表符分隔值。因此,每一行都可以使用第一行转换为键值对的映射。

我想通过地图中的某些键对这些文件的行进行排序,并将它们保存为已排序。

我读了这样的文件:

def parseTxtSimple(path: String):Array[String]=
{
   Source.fromFile(path).getLines().toArray
}

然后我使用compfunc对其进行排序:

val lines=parseTxtSimple(key+"/"+name)

// identify record keys from first line ( used by compfunc )
// strip is my own function that removes all \r and \n characters

val header=strip(lines.head).split("\t")

// sort lines

val lines_sorted:Array[String]=lines.tail.sortWith((leftE,rightE) => compfunc(leftE,rightE)).toArray

这是compfunc:

def compfunc(leftE:String,rightE:String):Boolean =
{
   val leftEr=(header zip strip(leftE).split("\t")).toMap
   val rightEr=(header zip strip(rightE).split("\t")).toMap
   var ratingLeftE:Float=0.0.toFloat
   var ratingRightE:Float=0.0.toFloat
   if(leftEr.contains("rating")) ratingLeftE=myToFloat(leftEr("rating"))
   if(rightEr.contains("rating")) ratingRightE=myToFloat(rightEr("rating"))
   ratingLeftE > ratingRightE
}

我的问题是排序很慢,即使是20 MB的文本文件,程序也会耗尽内存。

如果我读取文本文件而不是字符串数组作为值数组的数组(通过将每一行拆分为" \ t")并使用这些预处理数组而不是将它们拆分为& #34; \吨"在compfunc中,情况并不好。

整个计划在这里找到:

https://github.com/fideplayerslist/fideplayerslist/blob/master/players.scala

2 个答案:

答案 0 :(得分:1)

  def printOutput(header : Array[String], sorted : Array[String]) {
      println(header.mkString("\t"))
      sorted.foreach { x => println(x) }

  }
  def parseAndSortFile(lines: Array[String]): Unit = {
    val header = lines.head.split("\t")
    val ratingColumn = header.indexOf("ratings")
    if (ratingColumn == -1) {
      println("could not find ratings column");
    } else {
      val values = lines.tail.map { x => x.split("\t") }
      val sorted = (values.sortBy[Float] { x => x(ratingColumn).toFloat }).map(x => x.mkString("\t"))
      printOutput(header, sorted) // Replace this with whatever you want to do with the sorted data.
    }

  }

  import scala.io._
  val now = new Date().getTime
  parseAndSortFile(Source.fromFile(fileName).getLines.toArray)
  println(new Date().getTime-now)

这应该减少内存的使用并加快排序速度。但是,由于您在内存中进行排序,因此总是会有最大大小的上限。确保你的堆足够大。

我使用了2 GB的堆大小。

32 mb文件耗时16秒

65 mb文件需要34秒

98 mb文件需要61秒

注意:在执行计时时注释掉对printOutput的调用。

答案 1 :(得分:0)

我发现我可以对一个任意大的文件进行排序,只要文件中出现的所有排序键都适合内存并且可以按字符串数组排序。

这是怎么回事(非常不切实际,但适用于任何文件大小):

逐行解析文本文件,在当前行中建立排序键,打开一个以此排序键命名的文件,用于追加并附加该行,同时将键添加到键数组中(或者映射以避免多次添加。)

完成后,您拥有与排序键的出现值一样多的文件。

现在,对排序键数组和每个排序键进行排序,以便将属于此键的文件附加到排序文件中。最后,您将对大文件进行排序。

事实上在我的情况下,排序键只有几千种可能的值,所以这种方法肯定会起作用,只需要许多必要的文件操作就会使它变慢。