W skrócie
Wydaje się, że szybkim rozwiązaniem Twojego problemu jest zdefiniowanie REGEX lub FSA (automat skończony), który rozpoznaje wszystkie możliwe początki dokumentów (dozwolone są fałszywe alarmy, które w rzeczywistości nie odpowiadają dokumentowi). Następnie możesz uruchomić go bardzo szybko na podstawie danych wejściowych, aby określić następne miejsce, w którym dokument mógłby zacząć z niewielką liczbą błędów. Może to spowodować kilka błędnych pozycji na początku dokumentu, ale zostaną one rozpoznane przez analizator składni i porzucone.
Zatem automat skończony może być nazwą analizatora składni, której szukasz. :)
Problem
Zawsze trudno jest zrozumieć praktyczny problem, zwłaszcza gdy słownictwo może mieć wiele interpretacji. Słowo parsowanie lasu zostało wymyślone (afaik) do bezkontekstowego (CF) parsowania dwuznacznych zdań, które zawierają kilka parsowania. Można go w pewnym stopniu uogólnić na parsowanie sieci zdań lub na inne typy gramatyki. Stąd wszystkie odpowiedzi dotyczące parserów Earley, GLR, Marpa i pochodnych (istnieje wiele innych), które nie były istotne w tym przypadku.
Ale najwyraźniej nie to masz na myśli. Chcesz przeanalizować unikatowy ciąg znaków, który jest sekwencją jednoznacznych dokumentów, i uzyskać drzewo analizy składniowej dla każdego lub jakiegoś uporządkowanego przedstawienia, ponieważ tak naprawdę nie mówisz, jak zdefiniowana jest składnia twoich dokumentów, skąd się ona bierze formalny punkt widzenia języka. To, co masz, to algorytm i tabele, które wykonają parsowanie po uruchomieniu na początku dokumentu. Niech tak będzie.
Rzeczywistym problemem jest to, że strumień dokumentów zawiera znaczne śmieci, które oddzielają dokumenty. Wygląda na to, że Twoim problemem jest wystarczająco szybkie skanowanie tych śmieci. Twoja obecna technika polega na tym, aby zacząć od początku i spróbować skanować od pierwszego znaku i przeskakiwać do ponownego uruchomienia od następnego znaku, gdy tylko się nie powiedzie, aż do zeskanowania całego dokumentu. Następnie powtarzasz stwierdzanie od pierwszego znaku po zeskanowaniu dokumentu.
Takie jest również rozwiązanie sugerowane przez @amona w drugiej części jego odpowiedzi .
To może nie być bardzo szybkie rozwiązanie (nie mam możliwości przetestowania), ponieważ jest mało prawdopodobne, aby kod parsera został zoptymalizowany pod kątem bardzo skutecznego uruchamiania na początku dokumentu. W normalnym użyciu robi to tylko raz, dzięki czemu nie jest gorącym punktem z punktu widzenia optymalizacji. Dlatego Twoje umiarkowane szczęście z tego rozwiązania nie jest zbyt zaskakujące.
Tak więc naprawdę potrzebujesz algorytmu, który może szybko znaleźć początek dokumentu, który zaczyna się od dużej ilości śmieci. I masz szczęście: takie algorytmy istnieją. I jestem pewien, że o tym wiesz: nazywa się to poszukiwanie REGEX.
Proste rozwiązanie
Musisz przeanalizować specyfikację swoich dokumentów, aby dowiedzieć się, jak te dokumenty się zaczynają. Nie mogę dokładnie powiedzieć, jak to zrobić, ponieważ nie jestem pewien, jak formalnie zorganizowana jest ich specyfikacja składniowa. Być może wszystkie zaczynają się od jakiegoś słowa ze skończonej listy, być może pomieszanego z interpunkcją lub cyframi. To jest do sprawdzenia.
Musisz zdefiniować automat skończony (FSA) lub równoważnie dla większości programistów wyrażenie regularne (REGEX), które może rozpoznać kilka pierwszych znaków dokumentu: im więcej, tym lepiej, ale nie musi być bardzo duże (ponieważ może to zająć czas i przestrzeń). Powinno to być względnie łatwe do wykonania ze specyfikacji dokumentów i prawdopodobnie można to zrobić automatycznie za pomocą programu, który odczytuje specyfikację dokumentów.
Po utworzeniu wyrażenia regularnego możesz uruchomić go w strumieniu wejściowym, aby bardzo szybko przejść do początku pierwszego (lub następnego) dokumentu w następujący sposób:
Zakładam:
- docstart
jest wyrażeniem regularnym pasującym do początku wszystkich dokumentów
- search(regex, stream)
jest funkcją, która wyszukuje stream
pasujący podciąg regex
. Po zwróceniu strumień jest redukowany do swojego podrzędnego przyrostka rozpoczynającego się na początku pierwszego pasującego podciągu lub do pustego strumienia nie znaleziono dopasowania.
- parse(stream)
próbuje parsować dokument od początku strumienia (co pozostało z niego) i zwraca drzewo parsowania w dowolnym formacie lub nie powiedzie się. Po zwróceniu strumień jest redukowany do podrzędnego przyrostka, zaczynając od pozycji znajdującej się bezpośrednio za końcem analizowanego dokumentu. W przypadku niepowodzenia analizy wywołuje wyjątek.
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)
Pamiętaj, że usunięcie pierwszego znaku jest konieczne, aby następne wyszukiwanie nie znalazło ponownie tego samego dopasowania.
Oczywiście skrócenie strumienia jest obrazem. Może to być tylko indeks w strumieniu.
Ostatnia uwaga jest taka, że wyrażenie regularne nie musi być zbyt dokładne, o ile rozpoznaje wszystkie początki. Jeśli czasami rozpoznaje ciąg znaków, który nie może być początkiem dokumentu (fałszywie dodatni), jedyną karą jest koszt jednego bezużytecznego wywołania parsera.
Może to pomóc w uproszczeniu wyrażenia regularnego, jeśli jest to przydatne.
O możliwości szybszego rozwiązania
Powyższe rozwiązanie powinno działać całkiem dobrze w większości przypadków. Jeśli jednak masz naprawdę dużo śmieci i terabajtów pliku do przetworzenia, mogą istnieć inne algorytmy, które działają szybciej.
Pomysł wywodzi się z algorytmu wyszukiwania ciągów Boyera-Moore'a . Algorytm ten może bardzo szybko przeszukać strumień w poszukiwaniu pojedynczego ciągu, ponieważ wykorzystuje analizę strukturalną ciągu, aby pominąć czytanie większości strumienia, przeskakując fragmenty, nawet na nie nie patrząc. Jest to najszybszy algorytm wyszukiwania pojedynczego ciągu.
Trudność polega na tym, że jej dostosowanie do wyrażenia regularnego wyszukiwania, a nie pojedynczego łańcucha, wydaje się bardzo delikatne i może również nie działać, w zależności od funkcji wyrażenia regularnego, które rozważasz. To z kolei może zależeć od składni analizowanych dokumentów. Ale nie ufajcie mi zbytnio w tej sprawie, ponieważ nie miałem czasu na uważną lekturę znalezionych dokumentów.
Pozostawiam wam jeden lub dwa wskaźniki , które znalazłem w sieci, w tym jeden, który najwyraźniej jest recenzowanym artykułem badawczym , ale powinieneś rozważyć to jako bardziej spekulacyjne, być może badawcze, które należy wziąć pod uwagę tylko wtedy, gdy masz poważne problemy z wydajnością. I prawdopodobnie nie ma takiego programu półkowego, który to zrobiłby.