我们的一名工作人员丢失了他的邮箱,但幸运的是他以mbox格式转发了他的电子邮件。我需要以某种方式获取mbox文件中的所有消息并将它们喷入我们的技术支持数据库(因为它是一个自定义工具,没有可用的导入工具)。
我发现SharpMimeTools会分解邮件但不允许您在mbox文件中迭代一堆邮件。
有没有人知道一个不错的解析器,而不必学习RFC来写一个?
答案 0 :(得分:18)
我正在使用MIME& C#中的mbox解析器称为MimeKit。
它基于早期的MIME&我编写的mbox解析器(例如GMime)非常快(可以在大约1秒钟内解析1.2GB mbox文件中的每条消息)。
我还没有测试MimeKit的性能,但我在C#中使用了许多与C中使用的相同的技术。我怀疑它会比我的C实现慢,但是因为瓶颈是I / O和MimeKit编写为做最佳(4k)读取像GMime,它们应该非常接近。
您发现当前方法变慢的原因(StreamReader.ReadLine(),组合文本,然后将其传递给SharpMimeTools)是由于以下原因:
StreamReader.ReadLine()不是从文件中读取数据的最佳方式。虽然我确定StreamReader()执行内部缓冲,但它需要执行以下步骤:
A)将从文件读取的字节块转换为unicode(这需要迭代从磁盘读取的byte []中的字节,以将从流中读取的字节转换为unicode char [])。
B)然后它需要遍历其内部char [],将每个char复制到StringBuilder中,直到找到' \ n'。
就在那里,只有阅读线,你的mbox输入流至少有2次传递。更不用说正在进行的所有内存分配......
然后将您读过的所有行合并为一个单字符串。这需要对你的输入进行另一次传递(将从ReadLine()读取的每个字符串中的每个字符复制到StringBuilder中,大概是?)。
我们现在对输入文本进行了3次迭代,甚至还没有解析。
现在你把你的超级字符串交给使用SharpMimeMessageStream的SharpMimeTools ......(/ facepalm)是一个基于ReadLine()的解析器,它位于另一个执行字符集转换的StreamReader之上。这甚至可以在任何事情之前进行5次迭代。 SharpMimeMessageStream还有一种方法可以撤消"一个ReadLine()如果它发现它读得太远了。因此,可以合理地假设他正在扫描那些行中的部分至少两次。更不用说所有的字符串分配......呃。
对于每个标题,一旦SharpMimeTools有其行缓冲区,它就会分成字段&值。那是另一个传球。到目前为止,我们最多可以通过6次。
SharpMimeTools然后使用string.Split()(这是一个非常好的指示,这个mime解析器不符合标准)通过拆分'来标记地址标题。通过拆分&#39 ;;'和参数化标题(例如Content-Type和Content-Disposition)。那是另一个传球。 (我们现在最多7次通过。)
一旦它拆分,它就会对从string.Split()返回的每个字符串运行一个正则表达式匹配,然后每个rfc2047编码字令牌传递更多正则表达式,最后再对编码字字符集和有效负载进行另一次传递组件。到目前为止,我们在大部分输入上至少通过9或10次通过。
我放弃了我的考试,因为它已经超过GMime和MimeKit所需的2倍,而我知道我的解析器可以优化至少1通行证比他们少。
另外,作为旁注,任何解析字符串而不是byte [](或sbyte [])的MIME解析器永远不会很好。电子邮件的问题在于,野外的许多邮件客户端/脚本/等将在标头和邮件正文中发送未声明的8位文本。 unicode字符串解析器可能如何处理?提示:它不能。
2013-09-18更新:我已经让MimeKit到现在可用于解析mbox文件并成功设法解决问题,但它已经#39 ; s不如我的C库快。这是在iMac上测试的,因此I / O性能不如我在旧的Linux机器上那么好(这是GMime能够在~1s内解析类似大小的mbox文件):
[fejj@localhost MimeKit]$ mono ./mbox-parser.exe larger.mbox
Parsed 14896 messages in 6.16 seconds.
[fejj@localhost MimeKit]$ ./gmime-mbox-parser larger.mbox
Parsed 14896 messages in 3.78 seconds.
[fejj@localhost MimeKit]$ ls -l larger.mbox
-rw-r--r-- 1 fejj staff 1032555628 Sep 18 12:43 larger.mbox
正如您所看到的,GMime仍然相当快,但我对如何提高MimeKit解析器的性能有一些想法。事实证明,C#的fixed
语句非常昂贵,所以我需要重新使用它们。例如,昨天我做的a simple optimization从整体时间开始削减了大约2-3秒(如果我没记错的话)。
优化更新:通过替换:
,将性能提高了20%while (*inptr != (byte) '\n')
inptr++;
使用:
do {
mask = *dword++ ^ 0x0A0A0A0A;
mask = ((mask - 0x01010101) & (~mask & 0x80808080));
} while (mask == 0);
inptr = (byte*) (dword - 1);
while (*inptr != (byte) '\n')
inptr++;
优化更新:我终于可以通过切换使用Enum.HasFlag()并使用直接位屏蔽来最终使MimeKit像GMime一样快。
MimeKit现在可以在3.78s中解析相同的mbox流。
为了进行比较,SharpMimeTools花费超过20 分钟(为了测试这一点,我不得不将电子邮件拆分成单独的文件,因为SharpMimeTools无法解析mbox文件)。
另一个更新:我已经通过代码中的各种其他调整将其降至3.00秒。
答案 1 :(得分:2)
我不知道任何解析器,但mbox实际上是一种非常简单的格式。新邮件从以“发件人”(From + Space)开头的行开头,并在每封邮件的末尾附加一个空行。如果在电子邮件本身的一行开头出现“From”,则会引用它(通过添加'>')。
答案 2 :(得分:0)
如果你可以使用Python,那么标准库中就有one。我遗憾地找不到.NET。
答案 3 :(得分:0)
要读取.mbox文件,可以使用第三方库Aspose.Email。 该库是一套完整的电子邮件处理API,用于构建跨平台应用程序,这些应用程序无需使用Microsoft Outlook即可创建,处理,转换和传输电子邮件。
请看下面我提供的示例。
using(FileStream stream = new FileStream("ExampleMbox.mbox", FileMode.Open, FileAccess.Read))
{
using(MboxrdStorageReader reader = new MboxrdStorageReader(stream, false))
{
// Start reading messages
MailMessage message = reader.ReadNextMessage();
// Read all messages in a loop
while (message != null)
{
// Manipulate message - show contents
Console.WriteLine("Subject: " + message.Subject);
// Save this message in EML or MSG format
message.Save(message.Subject + ".eml", SaveOptions.DefaultEml);
message.Save(message.Subject + ".msg", SaveOptions.DefaultMsgUnicode);
// Get the next message
message = reader.ReadNextMessage();
}
}
}
易于使用。我希望这种方法能使您和其他搜索者满意。
我正在Aspose担任开发人员布道者。