我目前使用的是一种不太像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
的源代码并找到了一个很好的正则表达式模式,用于确定单个电子邮件消息的开头,但是在此之后我不知道该怎么做。我像下面这样创建了正则表达式: val MESSAGE_START = Pattern.compile(FromLinePatterns.DEFAULT, Pattern.MULTILINE);
我想做的事情(基于我目前所知):
- 从MBOX文件创建
FileInputStream
。 - 使用
Iterator.continually(stream.read())
遍历流。 - 使用
.takeWhile()
继续读取直到流的末尾。 - 使用类似
MESSAGE_START.matcher(someString).find()
的东西分块流,或者利用它找到分隔消息的索引。 - 读取创建的块或读取索引之间的位。
我觉得我应该能够使用map()
、find()
、filter()
和collect()
来完成这个任务,但是我被他们仅仅提供的Int
所困扰。
我该怎么做呢?
编辑:
在对此进行更多思考后,我想到了另一种描述我认为需要做的事情的方法:
我需要不断地从流中读取,直到得到一个匹配我的正则表达式的字符串
也许要
group
之前读取的字节?把它发送到某个地方进行处理
从范围中删除它,以便下次遇到匹配时不会分组
继续读取流,直到找到下一个匹配。
利润???
编辑2:
我觉得我已经接近了。使用这样一个方法可以让我得到一个迭代器的迭代器。然而,有两个问题:1. 这是浪费内存吗?这是否意味着所有东西都被读入内存中?2. 我仍然需要找出一种通过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)
split()
方法可能有效,但它似乎违反了迭代器的第一个规则:“在调用迭代器方法后,永远不应该再使用迭代器。最重要的两个例外也是唯一的抽象方法:next
和hasNext
。”(来自 Scaladocs 页面。) - jwvh