Вопрос

Один из наших сотрудников потерял свой почтовый ящик, но, к счастью, у него есть дамп его электронной почты в формате mbox.Мне нужно каким-то образом получить все сообщения внутри файла mbox и перенести их в нашу базу данных технической поддержки (поскольку это пользовательский инструмент, инструменты импорта недоступны).

Я нашел Острые измерительные инструменты который разбивает сообщение, но не позволяет вам перебирать кучу сообщений в файле mbox.

Кто-нибудь знает приличный синтаксический анализатор, который можно открыть без необходимости изучать RFC для его написания?

Это было полезно?

Решение

Я не знаю ни одного парсера, но mbox - это действительно очень простой формат.Новое электронное письмо начинается со строк, начинающихся с "From" (От + Пробел), и в конце каждого письма добавляется пустая строка.Если в самом электронном письме в начале строки встречается слово "От", оно берется в кавычки (добавляя '>').

Также смотрите Статья в Википедии по этой теме.

Другие советы

Я работаю над анализатором MIME и mbox на C #, который называется Имитировать.

Он основан на более ранних парсерах MIME и mbox, которые я написал (таких как GMime), которые были безумно быстрыми (могли разобрать каждое сообщение в файле mbox объемом 1,2 ГБ примерно за 1 секунду).

Я еще не тестировал MimeKit на производительность, но я использую многие из тех же методов в C #, которые я использовал в C.Я подозреваю, что это будет медленнее, чем моя реализация на C, но поскольку узким местом является ввод-вывод, а MimeKit написан для выполнения оптимального чтения (4k), как GMime, они должны быть довольно близки.

Причины, по которым вы находите свой текущий подход медленным (StreamReader.ReadLine(), объединение текста, а затем передача его SharpMimeTools), заключаются в следующих причинах:

  1. StreamReader.ReadLine() - не очень оптимальный способ чтения данных из файла.Хотя я уверен, что StreamReader() выполняет внутреннюю буферизацию, ему необходимо выполнить следующие шаги:

    A) Преобразовать блок байтов, считанных из файла, в юникод (для этого требуется выполнить итерацию по байтам в byte[], считанном с диска, чтобы преобразовать байты, считанные из потока, в символ юникода[]).

    B) Затем ему нужно выполнить итерацию по своему внутреннему символу[] , копируя каждый символ в StringBuilder, пока он не найдет ' '.

    Итак, прямо здесь, при простом чтении строк, у вас есть как минимум 2 прохода по вашему входному потоку mbox.Не говоря уже обо всех происходящих выделениях памяти...

  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 проходах по большей части входных данных к этому моменту.

Я отказываюсь идти дальше со своим экзаменом, потому что это уже более чем в 2 раза больше проходов, чем нужно GMime и MimeKit, и я знать мои парсеры можно было бы оптимизировать, чтобы сделать как минимум на 1 проход меньше, чем они делают.

Кроме того, в качестве дополнительного примечания, любой анализатор MIME, который анализирует строки вместо byte[] (или sbyte[]), никогда не будет очень хорошим.Проблема с электронной почтой заключается в том, что очень много почтовых клиентов / скриптов / etc в дикой природе отправляют необъявленный 8-битный текст в заголовках и текстах сообщений.Как может анализатор строк в юникоде возможно справиться с этим?Подсказка:этого не может быть.

2013-09-18 Обновление: Я довел MimeKit до такой степени, что теперь его можно использовать для синтаксического анализа файлов mbox, и мне успешно удалось устранить недостатки, но он работает далеко не так быстро, как моя библиотека C.Это было протестировано на iMac, поэтому производительность ввода-вывода не так хороша, как была бы на моей старой машине Linux (именно там GMime может анализировать файлы mbox аналогичного размера за ~ 1 секунду):

[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 # 's 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++;

Обновление оптимизации: Наконец-то я смог сделать MimeKit таким же быстрым, как GMime, отказавшись от использования Enum.Вместо этого я использовал HasFlag() и прямое битовое маскирование.

Теперь MimeKit может анализировать тот же поток mbox за 3,78 секунды.

Для сравнения, SharpMimeTools занимает более 20 минуты (чтобы проверить это, мне пришлось разделить электронные письма на отдельные файлы, потому что SharpMimeTools не может анализировать файлы mbox).

Еще одно Обновление: Я сократил его до 3,00 с без изменений с помощью различных других настроек по всему коду.

Если вы можете перейти к использованию Python, есть один в стандартной библиотеке.К сожалению, я не могу найти ничего для .NET.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top