2

我是 Scala 编程的新手。

我必须处理NLP任务。

我在处理Scala中的大型文本文件时遇到问题。

我已将 100+ MB 文件的整个文本读入内存(读入字符串)并且必须对其进行处理(我相信处理大型文本文件是自然语言处理中的一项常见任务)。

目标是计算给定字符串(即整个文件)中唯一子字符串/单词的数量。

我想在List对象中使用“ distinct ”方法,但是使用“ .split ”方法将字符串转换为列表会引发内存不足错误(“java.lang.OutOfMemoryError: Java heap space”错误)。

我想知道我是否可以在不使用 Scala 中使用字符串或正则表达式方法的列表的情况下完成这项任务?

4

3 回答 3

5

毫无疑问,可能必须增加默认的 JVM 堆大小。我非常怀疑使用 split 或任何其他基于 RE 的方法对于这么大的输入是否容易处理。同样,如果您将输入转换为 aList[Char]以利用精彩的集合库,您将看到内存需求过度增加;规模膨胀将至少是十进制数量级。

鉴于相对简单的分解(由空格或标点符号分隔的单词),我认为可能需要一个更平淡的解决方案。强制迭代字符串的字符(但不是通过隐式转换为任何类型的Seq[Char])并找到单词,将它们转储到mutable.Set[String]. 一方面,这将消除重复。或许使用 aBuffer[Char]来累积每个单词的字符,然后再将它们变成String要添加到Set[String].

这是一个削减:

package rrs.scribble

object  BigTextNLP {
  def btWords(bt: String): collection.mutable.Set[String] = {
    val btLength = bt.length
    val wordBuffer = collection.mutable.Buffer[Char]()
    val wordSet = collection.mutable.Set[String]()

    /* Assuming btLength > 0 */

    import bt.{charAt => chr}
    import java.lang.Character.{isLetter => l}

    var inWord = l(chr(0))

    (0 until btLength) foreach { i =>
      val c = chr(i)
      val lc = l(c)

      if (inWord)
        if (lc)
          wordBuffer += c
        else {
          wordSet += wordBuffer.mkString
          wordBuffer.clear
          inWord = false
        }
      else
        if (lc) {
          inWord = true
          wordBuffer += c
        }
    }

    wordSet
  }
}

在 REPL 中:

scala> import rrs.scribble.BigTextNLP._
import rrs.scribble.BigTextNLP._

scala> btWords("this is a sentence, maybe!")
res0: scala.collection.mutable.Set[String] = Set(this, maybe, sentence, is, a)
于 2013-01-24T02:27:20.607 回答
2

我假设您将文件作为List[String]内存,并且列表中的每个条目都是文件的一行。

val textStream = text.toStream
val wordStream = textStream.view.flatMap(s => s.split(" "))
val distinctWordStream = wordStream.foldLeft(Stream.empty[String])((stream, string) =>
  if (stream.contains(string)) stream else string #:: stream
)

首先,您创建一个 Stream,因此您不必处理整个字符串。下一步是创建一个视图并对其进行映射,因此每个字符串中只有一个单词而不是一行。最后你逐字折叠结果。如果一个词被包含,它将被删除。除了折叠,您还可以使用这一行:

val wordSet = wordStream.toSet

在这一点上,获取不同单词的数量应该是微不足道的。你只需要调用lengthsize为 Set。

于 2013-01-24T09:07:57.243 回答
1

看看这个讨论你的问题和不同方法的博客。

于 2013-01-24T19:45:48.130 回答