Pergunta

Um dos membros da nossa equipe perdeu sua caixa de correio, mas felizmente tem um despejo de seu e-mail em formato mbox. Eu preciso de alguma forma obter todas as mensagens dentro do arquivo mbox e esguicho-los em nosso banco de dados de suporte técnico (como uma ferramenta personalizada não há ferramentas de importação disponível).

Eu encontrei SharpMimeTools que divide uma mensagem, mas não permite que você iterate por meio de um monte de mensagens em um arquivo mbox.

Alguém sabe de um analisador decente isso é abrir sem ter que aprender a RFC para escrever um fora?

Foi útil?

Solução

Eu não sei qualquer analisador, mas mbox é realmente um formato muito simples. Um novo e-mail começa em linhas começando com "de" (A partir do espaço +) e uma linha de vácuo, está ligado à extremidade de cada um e-mail. Caso haja qualquer ocorrência de "From" no início de uma linha no próprio e-mail, este é citado fora (por prepending um '>').

Veja também entrada da Wikipedia sobre o tema .

Outras dicas

Eu estou trabalhando em um analisador MIME & mbox em C # chamado MimeKit .

É baseado em anteriores analisadores MIME & mbox que eu escrevi (como gmime ) que eram insanamente rápido (pôde analisar cada mensagem em um arquivo de 1.2GB mbox em cerca de 1 segundo).

Eu não testei MimeKit para o desempenho ainda, mas eu estou usando muitas das mesmas técnicas em C # que eu usei em C. Eu suspeito que vai ser mais lenta do que a minha aplicação C, mas desde que o gargalo é I / O e MimeKit é escrito para fazer ideal (4K) lê como gmime é, eles devem estar bem perto.

As razões que você está encontrando a sua abordagem atual para ser lento (StreamReader.ReadLine (), que combina o texto, em seguida, passá-lo fora para SharpMimeTools) são por causa das seguintes razões:

  1. StreamReader.ReadLine () não é uma maneira muito melhor de ler dados de um arquivo. Enquanto eu tenho certeza StreamReader () faz o buffer interno, que precisa fazer os seguintes passos:

    A) converter o bloco de bytes lidos a partir do ficheiro para Unicode (isto requer iteração sobre os bytes no byte [] lido a partir do disco para converter os bytes lidos do fluxo em um char Unicode []).

    B) Em seguida, ele necessita para repetir a sua carvão animal interno [], a cópia de cada caractere para um StringBuilder até encontrar um '\ n'.

    Então, ali mesmo, com linhas apenas de leitura, você tem pelo menos 2 passa sobre o seu fluxo de entrada mbox. para não mencionar todas as alocações de memória em andamento ...

  2. Então você combinar todas as linhas que você leu em um único mega-string. Isso exige uma outra passagem sobre sua entrada (copiando cada caractere de cada corda ler de ReadLine () em um StringBuilder, presumivelmente?).

    Estamos agora até 3 iterações sobre o texto de entrada e nenhuma análise foi mesmo aconteceu ainda.

  3. Agora você entregar o seu mega-string para SharpMimeTools que usa um SharpMimeMessageStream que ... (/ facepalm) é um ReadLine) (- analisador base que fica em cima de outro StreamReader que faz a conversão de charset. Isso faz 5 iterações antes de qualquer coisa é ainda analisado. SharpMimeMessageStream também tem uma forma de "desfazer" um ReadLine () se ele descobre que tem lido muito longe. Por isso, é razoável supor que ele está digitalizando mais de alguns dessas linhas, pelo menos duas vezes. para não mencionar todas as alocações de cordas acontecendo ... ugh.

  4. Para cada cabeçalho, uma vez SharpMimeTools tem o seu buffer de linha, ela se divide em campo e valor. Isso é uma outra passagem. Estamos até 6 passes até agora.

  5. SharpMimeTools então usa string.split () (que é uma indicação muito boa que este analisador mime não é compatível com os padrões) para tokenizar cabeçalhos de endereço por meio de separação em '', e cabeçalhos parametrizadas (como Content-Type e Content-Disposition) pela divisão em ';'. Isso é uma outra passagem. (Estamos agora até 7 passes.)

  6. Uma vez que divide aqueles que executa um jogo regex em cada cadeia devolvida a partir do string.split () e passa, então, mais regex por RFC2047 palavra-codificado token de, antes de finalmente fazer uma outra passagem sobre o charset palavra-codificado e payload componentes. Estamos falando de pelo menos 9 ou 10 passes em grande parte da entrada por este ponto.

Eu desistir de ir mais longe com o meu exame porque ele já mais de 2x é como muitos passes como gmime e MimeKit necessidade e eu sabe meus analisadores poderia ser otimizado para fazer pelo menos 1 menos passar do que eles fazer.

Além disso, como uma nota lateral, qualquer analisador MIME que analisa cordas em vez de byte [] (ou sbyte []) nunca vai ser muito bom. O problema com o e-mail é que muitos clientes de correio / scripts / etc na selva irá enviar mensagens de texto de 8 bits não declarado em cabeçalhos e corpos de mensagens. Como pode um analisador seqüência de caracteres Unicode possivelmente lidar com isso? Dica:. Não pode

2013/09/18 Update: eu comecei MimeKit ao ponto onde está agora utilizável paraanalisar arquivos de mbox e conseguiram com sucesso para elaborar as torções, mas não é quase tão rápido quanto a minha biblioteca C. Isso foi testado em um iMac, então eu / O desempenho não é tão bom como seria na minha máquina Linux de idade (que é onde gmime é capaz de analisar arquivos mbox de tamanhos semelhantes em ~ 1s):

[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

Como você pode ver, gmime ainda é um pouco mais rápido, mas eu tenho algumas ideias sobre como melhorar o desempenho do analisador de MimeKit. Acontece que as declarações fixed C # 's são muito caros, então eu preciso refazer meu uso deles. Por exemplo, uma simples otimização eu fiz ontem raspada sobre 2-3s do tempo total ( se bem me lembro).

Otimização Update: Apenas um melhor desempenho em mais 20%, substituindo:

while (*inptr != (byte) '\n')
    inptr++;

com:

do {
    mask = *dword++ ^ 0x0A0A0A0A;
    mask = ((mask - 0x01010101) & (~mask & 0x80808080));
} while (mask == 0);

inptr = (byte*) (dword - 1);
while (*inptr != (byte) '\n')
    inptr++;

Otimização Update:. eu era capaz de finalmente fazer MimeKit tão rápido quanto gmime mudando para longe do meu uso de Enum.HasFlag () e usando bit direta mascarando vez

MimeKit agora pode analisar o mesmo fluxo mbox em 3.78s.

Para comparação, SharpMimeTools leva mais do que 20 minutos (para testar isso, eu tinha que dividir os e-mails separados em arquivos separados porque SharpMimeTools não pode arquivos mbox de análise).

Outro Update:. eu comecei para baixo a 3.00s plana através de vários outros ajustes ao longo do código

Se você pode esticar a usar Python, há um na biblioteca padrão. Eu sou incapaz de encontrar qualquer para .NET com tristeza.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top