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

我找到了 SharpMime工具 它会分解一条消息,但不允许您迭代 mbox 文件中的一堆消息。

有谁知道有一个像样的开放式解析器,无需学习 RFC 即可编写出来?

有帮助吗?

解决方案

我不知道任何的解析器,但MBOX真的是一个非常简单的格式。一个新的电子邮件开始于开始的“From”(从+空格)线和空行被附加到每个邮件的末尾。如果有任何occurence“从”在电子邮件本身的线的开始,这是引用了(前面加上一个“>”)。

另请参见维基百科上关于该主题作品。

其他提示

我正在 C# 中开发一个 MIME 和 mbox 解析器,名为 哑剧套件.

它基于我编写的早期 MIME 和 mbox 解析器(例如 吉米梅)速度非常快(可以在大约 1 秒内解析 1.2GB mbox 文件中的每条消息)。

我尚未测试 MimeKit 的性能,但我在 C# 中使用了许多与 C 中相同的技术。我怀疑它会比我的 C 实现慢,但由于瓶颈是 I/O,并且 MimeKit 是为了像 GMime 一样进行最佳(4k)读取而编写的,因此它们应该非常接近。

您发现当前方法速度缓慢(StreamReader.ReadLine(),组合文本,然后将其传递给 SharpMimeTools)的原因如下:

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

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

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

    因此,只要读取行,您就至少可以通过 mbox 输入流 2 次。更不用说所有正在进行的内存分配......

  2. 然后,将读过的所有行组合成一个巨型字符串。这需要再次传递您的输入(大概将从 ReadLine() 读取的每个字符串中的每个字符复制到 StringBuilder 中?)。

    现在我们对输入文本进行了最多 3 次迭代,并且还没有进行任何解析。

  3. 现在,您将巨型字符串交给 SharpMimeTools,它使用 SharpMimeMessageStream,...(/facepalm) 是一个基于 ReadLine() 的解析器,位于另一个执行字符集转换的 StreamReader 之上。这使得在解析任何内容之前需要进行 5 次迭代。SharpMimeMessageStream 还可以在发现读取过多时“撤消”ReadLine()。所以可以合理地假设他正在扫描 一些 这些行至少两次。更不用说所有正在进行的字符串分配......啊。

  4. 对于每个标头,一旦 SharpMimeTools 拥有其行缓冲区,它就会分为字段和值。那是另一个关卡。到目前为止,我们已经通过了 6 次。

  5. 然后,SharpMimeTools 使用 string.Split() (这很好地表明该 mime 解析器不符合标准)通过拆分“,”来标记地址标头,并通过拆分来标记化参数化标头(例如 Content-Type 和 Content-Disposition)在 ';'。那是另一个关卡。(现在我们已经通过了 7 次。)

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

我放弃了进一步的检查,因为它已经是 GMime 和 MimeKit 所需数量的 2 倍多了,而且我 知道 我的解析器可以进行优化,使传递次数至少比它们少 1 次。

另外,顺便说一句,任何解析字符串而不是 byte[](或 sbyte[])的 MIME 解析器都不会很好。电子邮件的问题在于,许多邮件客户端/脚本/等会在标头和消息正文中发送未声明的 8 位文本。unicode 字符串解析器如何 可能 处理那个?暗示:不能。

2013年9月18日更新: 我已经使用 MimeKit 来解析 mbox 文件,并成功地解决了问题,但它的速度还不如我的 C 库。这是在 iMac 上进行测试的,因此 I/O 性能不如我的旧 Linux 机器(GMime 能够在大约 1 秒内解析类似大小的 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 语句非常昂贵,因此我需要重新设计它们的使用。例如, 一个简单的优化 我昨天把总时间缩短了大约 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.78 秒内解析相同的 mbox 流。

作为比较,SharpMimeTools 需要 20 多个 分钟 (为了测试这一点,我必须将电子邮件分成单独的文件,因为 SharpMimeTools 无法解析 mbox 文件)。

另一个更新: 通过对整个代码进行各种其他调整,我已将其降至 3.00 秒。

如果可以拉伸到使用Python,有一个在标准库。我无法找到任何对.NET黯然。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top