In poche parole
Sembra che la soluzione rapida al tuo problema sia quella di definire un REGEX, o un FSA (automa a stati finiti), che riconosca tutti i possibili inizi dei documenti (sono consentiti falsi positivi, che non corrisponderebbero effettivamente a un documento). È quindi possibile eseguirlo molto rapidamente sull'input per identificare il punto successivo in cui un documento potrebbe iniziare con pochi errori. Potrebbe causare alcune posizioni errate per l'avvio di un documento, ma verranno riconosciute dal parser e abbandonate.
Quindi Finite State Automaton potrebbe essere il nome del parser che stavi cercando. :)
Il problema
È sempre difficile comprendere un problema pratico, soprattutto quando il vocabolario può avere molte interpretazioni. La parola foresta di analisi è stata coniata (afaik) per l'analisi senza contesto (CF) di frasi ambigue che hanno diversi alberi di analisi. Può essere in qualche modo generalizzato all'analisi di un reticolo di frasi o ad altri tipi di grammatica. Da qui tutte le risposte su Earley, GLR, Marpa e sui parser derivati (ce ne sono molti altri) che non erano rilevanti in questo caso.
Ma a quanto pare non è quello che hai in mente. Vuoi analizzare una stringa univoca che è una sequenza di documenti non ambigui e ottenere un albero di analisi per ciascuno o un qualche tipo di rappresentazione strutturata, dal momento che non dici davvero come viene definita la sintassi dei tuoi documenti, da dove proviene un punto di vista formale del linguaggio. Quello che hai è un algoritmo e tabelle che eseguiranno il lavoro di analisi quando avviato all'inizio di un documento. Così sia.
Il vero problema è che il flusso di documenti contiene una notevole spazzatura che separa i documenti. E sembra che la tua difficoltà sia scansionare questa spazzatura abbastanza velocemente. La tua tecnica attuale è quella di iniziare all'inizio, provare a scansionare dal primo carattere e saltare al riavvio al carattere successivo ogni volta che fallisce, fino a quando non viene acquisito un intero documento. Quindi ripeti affermando il primo carattere dopo che il documento è stato appena scansionato.
Questa è anche la soluzione suggerita da @amon nella seconda parte della sua risposta .
Potrebbe non essere una soluzione molto veloce (non ho modo di testare), perché è improbabile che il codice del parser sia ottimizzato per essere avviato in modo molto efficiente all'inizio di un documento. Nell'uso normale lo fa solo una volta, quindi non è un hot spot dal punto di vista dell'ottimizzazione. Quindi, la tua moderata felicità con questa soluzione non è troppo sorprendente.
Quindi ciò di cui hai veramente bisogno è un algoritmo che possa trovare rapidamente l'inizio di un documento che inizia con una massa di immondizia. E tu sei fortunato: tali algoritmi esistono. E sono sicuro che lo sai: si chiama alla ricerca di un REGEX.
La soluzione semplice
Quello che devi fare è analizzare le specifiche dei tuoi documenti per scoprire come iniziano questi documenti. Non posso dirti esattamente come, poiché non sono sicuro di come siano organizzate formalmente le loro specifiche di sintassi. Forse iniziano tutti con una parola da un elenco finito, eventualmente mescolato con alcuni segni di punteggiatura o numeri. Questo è per te da controllare.
Quello che devi fare è definire un automa a stati finiti (FSA), o equivalentemente per la maggior parte dei programmatori un'espressione regolare (REGEX) in grado di riconoscere i primi caratteri di un documento: più sono, meglio è, ma non è necessario che sia molto grande (poiché ciò potrebbe richiedere tempo e spazio). Questo dovrebbe essere relativamente facile da fare dalle specifiche dei tuoi documenti e probabilmente può essere fatto automaticamente con un programma che legge le specifiche dei tuoi documenti.
Una volta prodotto regexp, è possibile eseguirlo sul flusso di input per arrivare molto rapidamente all'inizio del primo (o successivo) documento come segue:
Presumo:
- docstart
è una regex che corrisponde all'inizio di tutti i documenti
- search(regex, stream)
è una funzione che cerca stream
una sottostringa che corrisponde regex
. Quando ritorna, il flusso viene ridotto al suo substream di suffisso a partire dall'inizio della prima sottostringa corrispondente, oppure al flusso vuoto se non viene trovata alcuna corrispondenza.
- parse(stream)
tenta di analizzare un documento dall'inizio del flusso (ciò che ne rimane) e restituisce l'albero di analisi in qualsiasi formato o non riesce. Quando ritorna, il flusso viene ridotto al suo substream di suffisso a partire dalla posizione immediatamente successiva alla fine del documento analizzato. Chiama un'eccezione se l'analisi non riesce.
forest = empty_forest
search(docstart, stream)
while stream is not empty:
try:
forest = forest + parse(stream)
except
remove first character from stream
search(docstart, stream)
Si noti che la rimozione del primo carattere è necessaria in modo che la ricerca successiva non trovi nuovamente la stessa corrispondenza.
Naturalmente, l'accorciamento del flusso è un'immagine. Potrebbe essere solo un indice sullo stream.
Un'ultima nota è che il tuo regex non deve essere troppo preciso, purché riconosca tutti gli inizi. Se a volte riconosce una stringa che non può essere l'inizio di un documento (falso positivo), l'unica penalità è il costo di una chiamata inutile al parser.
In modo che possa eventualmente aiutare a semplificare la regex, se utile.
Sulla possibilità di una soluzione più rapida
La soluzione di cui sopra dovrebbe funzionare abbastanza bene nella maggior parte dei casi. Tuttavia, se hai davvero un sacco di immondizia e terabyte di file da elaborare, potrebbero esserci altri algoritmi che funzionano più velocemente.
L'idea è derivata dall'algoritmo di ricerca delle stringhe di Boyer-Moore . Questo algoritmo può cercare uno stream per una singola stringa in modo estremamente rapido perché utilizza un'analisi strutturale della stringa per saltare la lettura della maggior parte dello stream, saltando su frammenti senza nemmeno guardarli. È l'algoritmo di ricerca più veloce per una singola stringa.
La difficoltà è che il suo adattamento alla ricerca di regex, piuttosto che a una singola stringa, sembra molto delicato e potrebbe non funzionare altrettanto bene, a seconda delle caratteristiche della regex che stai prendendo in considerazione. Ciò a sua volta potrebbe dipendere dalla sintassi dei documenti che stai analizzando. Ma non fidarti troppo di me, poiché non ho avuto il tempo di leggere attentamente i documenti che ho trovato.
Ti lascio con uno o due suggerimenti che ho trovato sul web, incluso uno che è apparentemente un documento di ricerca arbitrato , ma dovresti considerare questo come più speculativo, possibilmente ricercato, da considerare solo se hai avuto forti problemi di prestazioni. E probabilmente non c'è nessuno dei programmi sugli scaffali che lo farà.