我是Scala编程的新手。
我必须处理 NLP 任务。
我在 Scala 中处理大型文本文件时遇到问题。
我已经将100+ M.B文件的整个文本读入内存(转换为字符串)并且必须处理它(我相信处理大型文本文件是自然语言处理中的常见任务)。
目标是计算给定字符串中唯一子字符串/单词的数量(这是整个文件)。
我想在 List 对象中使用“ distinct ”方法,但使用“ .split将字符串转换为列表 “方法引发内存不足错误(”java.lang.OutOfMemoryError:Java堆空间“错误)。
我想知道我是否可以在Scala中使用String或Regular Expression方法使用列表来完成此任务?
答案 0 :(得分:5)
默认的JVM堆大小可能必须增加。我非常怀疑使用拆分或任何其他基于RE的方法对于那么大的输入都是易处理的。同样,如果将输入转换为List[Char]
以利用精彩的集合库,您将会看到内存需求的过度增加;规模通胀率将至少为十进制数量级。
鉴于相对简单的分解(由空格或标点符号分隔的单词),我认为可能需要更平淡的解决方案。命令性地对字符串的字符进行迭代(但不是通过隐式转换为任何类型的Seq[Char]
)并找到单词,将它们转储到mutable.Set[String]
中。一方面,这将消除重复。也许可以使用Buffer[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)
答案 1 :(得分: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,这样您就不必处理整个String。下一步是创建一个View并对其进行maping,因此每个String中只有一个单词而不是一行。最后,您逐字折叠结果。如果还包含一个单词,它将被删除。而不是折叠你也可以使用这一行:
val wordSet = wordStream.toSet
此时获取不同单词的数量应该是微不足道的。您只需为“设置”调用length
或size
。
答案 2 :(得分:1)