Domanda

Uno dei nostri membri del personale ha perso la sua casella di posta, ma ha fortunatamente una discarica della sua e-mail in formato mbox. Ho bisogno di ottenere in qualche modo tutti i messaggi all'interno del file mbox e li schizzare nel nostro database di supporto tecnico (come uno strumento personalizzato non ci sono strumenti di importazione disponibili).

Ho trovato SharpMimeTools che rompe un messaggio, ma non consente di eseguire iterazioni attraverso un mazzo di messaggi in un file mbox.

Qualcuno sa di un dignitoso i thats parser aperte senza dover imparare il RFC di scrivere uno fuori?

È stato utile?

Soluzione

Non conosco alcun parser, ma Mbox è davvero un formato molto semplice. Una nuova email comincia su linee che iniziano con "From" (da + Spazio) e una linea vuota è attaccata alla fine di ogni posta. Se ci dovessero essere occorrenza di "From" all'inizio di una riga nella mail stessa, questo è citato out (anteponendo un '>').

ingresso di Wikipedia sul tema .

Altri suggerimenti

Sto lavorando su un parser MIME & Mbox in C # chiamato MimeKit .

Si basa su precedenti parser MIME & mbox che ho scritto (come GMime ) che era follemente veloce (potrebbe analizzare ogni messaggio in un file mbox 1,2 GB in circa 1 secondo).

Non ho ancora testato MimeKit per le prestazioni, ma sto utilizzando molte delle stesse tecniche in C # che ho usato in C. Ho il sospetto che sarà più lento di mia implementazione C, ma dato che il collo di bottiglia è di I / O e MimeKit è scritto di fare ottimale (4k) si legge come GMime è, dovrebbero essere abbastanza vicino.

Le ragioni si stanno trovando il vostro approccio attuale ad essere lento (StreamReader.ReadLine (), che unisce il testo, quindi spacciandolo per SharpMimeTools) sono a causa dei seguenti motivi:

  1. StreamReader.ReadLine () non è un modo molto ottimale di lettura dei dati da un file. Mentre sono sicuro che StreamReader () non buffer interno, ha bisogno di effettuare le seguenti operazioni:

    A) Convertire il blocco di byte letti dal file in unicode (questa iterazione richiede sopra i byte nel byte [] letti dal disco di convertire i byte letti dal flusso in un char unicode []).

    B) quindi ha bisogno di iterare il suo carattere interno [], la copia di ogni carattere in uno StringBuilder finché non trova un '\ n'.

    Così proprio lì, con le linee di lettura giusta, di avere almeno 2 passaggi sopra il vostro flusso di input mbox. Per non parlare di tutte le allocazioni di memoria in corso ...

  2. Poi si combinano tutte le linee che hai letto in un unico mega-stringa. Questo richiede un altro passaggio sopra il vostro ingresso (la copia di ogni carattere da ogni stringa leggere da ReadLine () in uno StringBuilder, presumibilmente?).

    Siamo ora fino a 3 iterazioni oltre il testo di input e non di analisi nemmeno è ancora successo.

  3. Ora la mano fuori il tuo mega-stringa SharpMimeTools che utilizza uno SharpMimeMessageStream che ... (/ facepalm) è un ReadLine () - parser based che si trova sulla cima di un altro StreamReader che non charset conversione. Che rende 5 iterazioni prima di tutto a tutti è ancora analizzato. SharpMimeMessageStream ha anche un modo per "annullare" un ReadLine () se si accorge di aver letto troppo lontano. Quindi è ragionevole supporre che egli è la scansione su alcuni di quelle linee almeno due volte. Per non parlare di tutte le allocazioni di stringhe in corso ... ugh.

  4. Per ogni intestazione, una volta SharpMimeTools ha il suo buffer di linea, si divide in campo e valore. Questo è un altro passaggio. Siamo fino a 6 passaggi finora.

  5. SharpMimeTools quindi utilizza String.split () (che è una buona indicazione che questo mimo parser non è conforme agli standard) per tokenize intestazioni di indirizzo, dividendo su '' e le intestazioni con parametri (come Content-Type e Content-Disposition), dividendo il ';'. Questo è un altro passaggio. (Siamo ora fino a 7 passaggi.)

  6. Una volta che divide coloro corre un match regex su ogni stringa restituita dal String.split () e quindi più regex passa per RFC2047 codificati-parola token prima infine di effettuare un altro passaggio sulla parola-codificati charset e payload componenti. Stiamo parlando di almeno 9 o 10 passaggi su gran parte dell'ingresso da questo punto.

mi arrendo andare più in là con il mio esame, perché è già più di 2 volte il maggior numero di passaggi come GMime e MimeKit bisogno e I so i miei parser potrebbero essere ottimizzati per fare almeno 1 meno passaggio di loro fare.

Inoltre, come un lato nota, qualsiasi parser MIME che analizza le stringhe invece di byte [] (o sbyte []) non è mai sta per essere molto buona. Il problema con la posta elettronica è che così tanti client di posta elettronica / scripts / etc in natura invierà il testo 8bit non dichiarato nelle intestazioni e il corpo dei messaggi. Come può un parser stringa unicode eventualmente gestire questo? Suggerimento:. Non possono

2013/09/18 Aggiornamento: ho ottenuto MimeKit al punto in cui è ora utilizzabile perl'analisi di file mbox e sono riusciti con successo a lavorare i nodi, ma non è quasi veloce come la mia libreria C. Questo è stato testato su un iMac così prestazioni di I / O non è così buono come sarebbe sulla mia vecchia macchina Linux (che è dove GMime è in grado di analizzare i file mbox di dimensioni simili in ~ 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

Come si può vedere, GMime è ancora un po 'più veloce, ma ho alcune idee su come migliorare le prestazioni del parser di MimeKit. Si scopre che le dichiarazioni C # s 'fixed sono piuttosto costosi, quindi ho bisogno di rielaborare il mio utilizzo di loro. Ad esempio, una semplice ottimizzazione ho fatto ieri rasato circa 2-3s dal tempo complessivo ( se non ricordo male).

Ottimizzazione Aggiornamento: Just migliorato le prestazioni di un altro 20% sostituendo:

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

con:

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

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

Ottimizzazione Aggiornamento: sono stato in grado di fare finalmente MimeKit veloce come GMime passando dal mio uso di Enum.HasFlag () e utilizzando po diretta mascheramento invece

.

MimeKit può ora analizzare lo stesso flusso mbox in 3.78s.

Per fare un confronto, SharpMimeTools prende più di 20 minuti (per testare questo, ho dovuto dividere i messaggi di posta elettronica a parte in file separati, perché SharpMimeTools non possono analizzare i file mbox).

Un altro aggiornamento:. ho ottenuto giù a 3.00s piatto tramite varie altre modifiche in tutto il codice

Se si può allungare a utilizzare Python, c'è uno nella libreria standard. Sono in grado di trovare qualsiasi for .NET tristemente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top