在C#中读取mbox文件

时间:2009-05-24 12:47:17

标签: c# email mime mbox

我们的一名工作人员丢失了他的邮箱,但幸运的是他以mbox格式转发了他的电子邮件。我需要以某种方式获取mbox文件中的所有消息并将它们喷入我们的技术支持数据库(因为它是一个自定义工具,没有可用的导入工具)。

我发现SharpMimeTools会分解邮件但不允许您在mbox文件中迭代一堆邮件。

有没有人知道一个不错的解析器,而不必学习RFC来写一个?

4 个答案:

答案 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)是由于以下原因:

  1. StreamReader.ReadLine()不是从文件中读取数据的最佳方式。虽然我确定StreamReader()执行内部缓冲,但它需要执行以下步骤:

    A)将从文件读取的字节块转换为unicode(这需要迭代从磁盘读取的byte []中的字节,以将从流中读取的字节转换为unicode char [])。

    B)然后它需要遍历其内部char [],将每个char复制到StringBuilder中,直到找到' \ n'。

    就在那里,只有阅读线,你的mbox输入流至少有2次传递。更不用说正在进行的所有内存分配......

  2. 然后将您读过的所有行合并为一个单字符串。这需要对你的输入进行另一次传递(将从ReadLine()读取的每个字符串中的每个字符复制到StringBuilder中,大概是?)。

    我们现在对输入文本进行了3次迭代,甚至还没有解析。

  3. 现在你把你的超级字符串交给使用SharpMimeMessageStream的SharpMimeTools ......(/ facepalm)是一个基于ReadLine()的解析器,它位于另一个执行字符集转换的StreamReader之上。这甚至可以在任何事情之前进行5次迭代。 SharpMimeMessageStream还有一种方法可以撤消"一个ReadLine()如果它发现它读得太远了。因此,可以合理地假设他正在扫描那些行中的部分至少两次。更不用说所有的字符串分配......呃。

  4. 对于每个标题,一旦SharpMimeTools有其行缓冲区,它就会分成字段&值。那是另一个传球。到目前为止,我们最多可以通过6次。

  5. SharpMimeTools然后使用string.Split()(这是一个非常好的指示,这个mime解析器不符合标准)通过拆分'来标记地址标题。通过拆分&#39 ;;'和参数化标题(例如Content-Type和Content-Disposition)。那是另一个传球。 (我们现在最多7次通过。)

  6. 一旦它拆分,它就会对从string.Split()返回的每个字符串运行一个正则表达式匹配,然后每个rfc2047编码字令牌传递更多正则表达式,最后再对编码字字符集和有效负载进行另一次传递组件。到目前为止,我们在大部分输入上至少通过9或10次通过。

  7. 我放弃了我的考试,因为它已经超过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”,则会引用它(通过添加'>')。

另见Wikipedia's entry on the topic

答案 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担任开发人员布道者。