我目前正在使用一种不太像Scala的方法来解析大型Unix邮箱文件。我仍在学习该语言,并想挑战自己以找到更好的方法,但是,我不相信我对Iterator
可以做什么以及如何有效使用它有扎实的了解。
我当前正在使用 org.apache.james.mime4j
,并且我使用org.apache.james.mime4j.mboxiterator.MboxIterator
从文件中获取java.util.Iterator
,如下所示:
// registers an implementation of a ContentHandler that
// allows me to construct an object representing an email
// using callbacks
val handler: ContentHandler = new MyHandler();
// creates a parser that parses a SINGLE email from a given InputStream
val parser: MimeStreamParser = new MimeStreamParser(configBuilder.build());
// register my handler
parser.setContentHandler(handler);
// Get a java.util.Iterator
val iterator = MboxIterator.fromFile(fileName).build();
// For each email, process it using above Handler
iterator.forEach(p => parser.parse(p.asInputStream(Charsets.UTF_8)))
根据我的理解,Scala Iterator
更加健壮,并且可能具有更多类似的处理能力,特别是因为我将无法始终将整个文件容纳在内存中。
我需要构建自己的MboxIterator
版本。我翻阅了MboxIterator
的源代码,能够找到一个很好的RegEx模式来确定各个电子邮件的开头,但是,从现在开始,我一直在空白。
我这样创建了RegEx:
val MESSAGE_START = Pattern.compile(FromLinePatterns.DEFAULT, Pattern.MULTILINE);
我想做的事情(根据我到目前为止的了解):
FileInputStream
。Iterator.continually(stream.read())
浏览流.takeWhile()
继续阅读直到流结束MESSAGE_START.matcher(someString).find()
之类的内容对流进行分块,或使用它来查找与消息分开的索引我觉得我应该可以使用map()
,find()
,filter()
和collect()
来完成此任务,但是我被事实抛弃了他们只给我Int
一起工作。
我该怎么做?
编辑:
在对该主题进行了更多思考之后,我想到了另一种描述我思考我需要做的事情:
我需要继续从流中读取内容,直到获得与RegEx匹配的字符串为止
也许group
是先前读取的字节?
将其发送以在某处进行处理
以某种方式将其从作用域中删除,以便下次我遇到比赛时不会将其分组
继续读取流,直到找到下一个匹配项。
利润?
编辑2:
我想我越来越近了。使用这样的方法可以让我得到一个迭代器。但是,有两个问题:1.这是否浪费内存?这是否意味着所有内容都已读入内存? 2.我仍然需要找出一种将{em>除以match
的方法,但是仍将其包含在返回的迭代器中。
def split[T](iter: Iterator[T])(breakOn: T => Boolean):
Iterator[Iterator[T]] =
new Iterator[Iterator[T]] {
def hasNext = iter.hasNext
def next = {
val cur = iter.takeWhile(!breakOn(_))
iter.dropWhile(breakOn)
cur
}
}.withFilter(l => l.nonEmpty)
答案 0 :(得分:0)
如果我的理解正确,那么您想懒散地分割由正则表达式可识别的模式分隔的大文件。
您可以尝试为每个请求返回一个Iterator
,但是正确的迭代器管理并不容易。
我倾向于对客户端隐藏所有文件和迭代器管理。
class MBox(filePath :String) {
private val file = io.Source.fromFile(filePath)
private val itr = file.getLines().buffered
private val header = "From .+ \\d{4}".r //adjust to taste
def next() :Option[String] =
if (itr.hasNext) {
val sb = new StringBuilder()
sb.append(itr.next() + "\n")
while (itr.hasNext && !header.matches(itr.head))
sb.append(itr.next() + "\n")
Some(sb.mkString)
} else {
file.close()
None
}
}
测试:
val mbox = new MBox("so.txt")
mbox.next()
//res0: Option[String] =
//Some(From MAILER-DAEMON Fri Jul 8 12:08:34 2011
//some text AAA
//some text BBB
//)
mbox.next()
//res1: Option[String] =
//Some(From MAILER-DAEMON Mon Jun 8 12:18:34 2012
//small text
//)
mbox.next()
//res2: Option[String] =
//Some(From MAILER-DAEMON Tue Jan 8 11:18:14 2013
//some text CCC
//some text DDD
//)
mbox.next() //res3: Option[String] = None
每个打开的文件中只有一个Iterator
,并且仅在其上调用安全方法。仅在请求时才实现(加载)文件文本,并且客户端将仅获取请求的内容(如果有)。如果更适用,则可以将每一行作为集合String
的一部分返回,而不是将它们放在一个长Seq[String]
中。