映射元组的大列表时获取java OutOfMemoryError

时间:2017-02-24 14:44:52

标签: scala

我的功能只是一个上传的csv文件,有一些格式问题,并调用另一个函数来清理它并修复它的格式。

但是当我用一个非常大的文件(作为字符串)调用我的清洁功能时,我收到错误:

  

[错误] [24/02/2017] [application-scheduler-1]   [ActorSystem(应用程序)]来自线程的未捕获错误   [application-scheduler-1]从那时起关闭JVM   ' akka.jvm-出口上致命错误'已启用    java.lang.OutOfMemoryError:超出GC开销限制

这是它失败的地方:

  def clean(fileStr: String): String = {

    val zippedWithIndex = fileStr.zipWithIndex

    // i need to map it like this for the next stage of my cleaning
    val indexCharMap = zippedWithIndex.map(cur => (cur._2,cur._1)).toMap

    // my string builder
    val builder = new StringBuilder()


...

}

它在zippedWithIndex.map失败,因为文件非常大(fileStr.length是10948026)

我需要它导致我对zippedWithIndex进行迭代,我需要能够检查前后的char(例如:indexCharMap.get(chr._2 - 1))

它只是想了一会儿然后抛出上面的例外。

什么是更好的解决方法?

(除了转移到流,这将是本产品的第二个版本)

谢谢!

所以现在它的工作原理如下:

我需要一个合适的csv文件,即:

"Header1","Header2","Header3"

"value1","value2","value3"

但是我得到的文件是从一些我无法访问的系统中获取并且格式化被破坏,这意味着我可以得到这样的文件:

"Header1","Header2","Header3"

"val"ue1","val"ue2","val"ue"3"

因为我在获取的值中可以引用并且将它们取出的系统没有进行适当的转义,这应该使它看起来像这样:

"Header1","Header2","Header3"

"val""ue1","val""ue2","val""ue""3"

因为csv格式的工作方式是,如果你想要具有相当的内部价值,你需要加上双引号。

所以要通过zippedWithIndex迭代修复它,逻辑如下:

 zippedWithIndex.foreach( chr => {

 builder.append(chr._1)

if(!currentlyInsideValue && begginingOfValue(indexCharMap.get(chr._2 - 1)))
        currentlyInsideValue = true

 else if (currentlyInsideValue && endingValue(indexCharMap.get(chr._2 + 1)))
        currentlyInsideValue = false
      else
        builder.append('"')

}

2 个答案:

答案 0 :(得分:2)

停止在toMap中使用zippedWithIndex.map(cur => (cur._2,cur._1)).toMap功能。这就是 GC开销限制超出错误的原因。懒惰地处理整个文件,例如如下所示

val lines = for {
    (line,index) <- Source.fromFile("output.txt").getLines.zipWithIndex
} yield (line -> index)

def cleaningFunction(currentLine: String, nextLine: String): String = {
   //hypothetical cleaning function
}

val result =  lines.toSeq zip lines.toSeq.tail // stream based sequence to access before/after lines

result.map((x,y) => cleaningFunction(x,y))

行现在将具有您文件的已清理内容,以后可以使用。

答案 1 :(得分:0)

看起来您不需要将整个文件保存在内存中,一次只需要一行文本。尝试类似下面的程序。在cleanOneLine中编写自己的代码,以在一行文本中修改引号

import scala.io.Codec
import scala.io.Source
import java.io.PrintWriter

object CSVCleaner {
  val fileName = "Broken.csv"
  val output = "Fixed.csv"

  def main(args: Array[String]) {
    clean(fileName)
  }

  def clean(fileStr: String) {
    val pw = new PrintWriter(output)
    val source = Source.fromFile(fileName)(Codec.ISO8859)
    source.getLines.foreach { line =>
      val clean = cleanOneLine(line)
      pw.print(clean + "\n")
    }
    pw.flush
    pw.close
    source.close
  }

  def cleanOneLine(line: String): String = {
    // your code here
    "put, your, code, here" // ToDo
  }
}