Problem
Chciałbym napisać w Javie program, który wyciąga z tekstu cytaty, oraz nazwę osób je mówiących.
Wydaje mi się to rzeczą specyficzną dla danego autora, toteż postanowiłem skoncentrować się na utworze "Pan Tadeusz". Założyłem, że będzie to probierz, czy ogólnie program działa, czy nie.
Do wyszukania w tekście odpowiednich fragmentów postanowiłem użyć wyrażeń regularnych. Miałem do wyboru też parser. Domyślam się, że wyrażenia regularne nie są najlepsze do wyszukiwania tekstu, ale ja stoję na stanowisku, że jeśli czegoś nie da się zrobić jakimś narzędziem, to dlatego, że ma się za mało wyobraźni – oraz po prostu je znam, a parsera nigdy nie pisałem.
Ale mam problem, ponieważ przy uruchomieniu programu pojawia mi się java.lang.StackOverflowError
. Używam OpenJDK 11, ale kombinuję, że może to mieć coś wspólnego z tą funkcjonalnością Javy: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=5050507, opisaną tutaj: https://www.javaworld.com/article/2077757/core-java/optimizing-regular-expressions-in-java.html?page=4
Pytanie
Czy kombinować dalej z wyrażeniami regularnymi, czy nie?
1. Jeśli tak, to jak poradzić sobie z tym błędem?
2. Jeśli nie, to jak inaczej to napisać?
Przykład
Dla tekstu
Dano trzecią potrawę. Wtem pan Podkomorzy,
Wlawszy kropelkę wina w szklankę panny Róży,
A młodszej przysunąwszy z talerzem ogórki,
Rzekł: "Muszę ja wam służyć, moje panny córki,
Choć stary i niezgrabny". Zatem się rzuciło
Kilku młodych od stołu i pannom służyło.
program powinien wyodrębnić (docelowo, bo jeszcze nie wszystko napisałem)
Wtem pan Podkomorzy,
Wlawszy kropelkę wina w szklankę panny Róży,
A młodszej przysunąwszy z talerzem ogórki,
Rzekł: "Muszę ja wam służyć, moje panny córki,
Choć stary i niezgrabny".
Mój aktualny kod
Wykomentowałem fragmenty niedotyczące mojego pytania.
// tu wczytywanie danych z wejścia do zmiennej inputString
List<SemanticMarker> semanticMarkersRegexes = new ArrayList<>(Arrays.asList(new SemanticMarker("powiedział(?:a|o)?"), new SemanticMarker("rzekł(?:a|o)?"), new SemanticMarker("mówił(?:a|o)?")));
String semanticMarkersRegexesAlternative = String.join("|", semanticMarkersRegexes.stream().map(x -> x.getMarker()).collect(Collectors.toList()));
// not-escaped regex: [^\n]*(semanticMarkersRegexesAlternative)(?:[^"\n]|[^"\n]\n(?!\n))+"(?:[^"\n]|[^"\n]\n(?!\n)|[^"\n]\n\n")+"
String semanticMarkerBeforeRegex = "(?is)[^\\n]*(" + semanticMarkersRegexesAlternative + ")(?:[^\"\\n]|[^\"\\n]\\n(?!\\n))+\"(?:[^\"\\n]|[^\"\\n]\\n(?!\\n)|[^\"\\n]\\n\\n\")+\"";
// tu dopasowywanie inputString względem semanticMarkerBeforeRegex
// not-escaped regex: (?is)"(?:[^"\n]|[^"\n]\n(?!\n)|[^"\n]\n\n")+"
String allQuotesRegex = "(?is)\"(?:[^\"\\n]|[^\"\\n]\\n(?!\\n)|[^\"\\n]\\n\\n\")+\"";
// tu dopasowywanie inputString względem allQuotesRegex
// not-escaped regex: (?is)"(?:[^"\n]|[^"\n]\n(?!\n)|[^"\n]\n\n")+"(?:[^"\n]|[^"\n]\n(?!\n))+(semanticMarkersRegexesAlternative)[^"\n]*
String semanticMarkerAfterRegex = "(?is)\"(?:[^\"\\n]|[^\"\\n]\\n(?!\\n)|[^\"\\n]\\n\\n\")+\"(?:[^\"\\n]|[^\"\\n]\\n(?!\\n))+(" + semanticMarkersRegexesAlternative + ")[^\"\\n]*";
// tu dopasowywanie inputString względem semanticMarkerAfterRegex
// tu wypisywanie danych na wyjście
Co ciekawe, Java wydaje się robić problem z negative lookahead. Gdy testuję tylko pierwsze wyrażenie regularne, semanticMarkerBeforeRegex
, i oba fragmenty (?!\\n)
zamieniam na [^\\n]
, Java zwraca wyniki (nie wiem co prawda, czy poprawne, ale sądzę, że to inna sprawa).
Jeśli kod wygląda skomplikowanie lub dziwnie, to dlatego, że nie jestem jeszcze dobry w Javie. Każdą uwagę przyjmę. :)
UPDATE
Może należałoby wspomnieć, że tekst, na którym dopasowywałem wyrażenie regularne, to cała pierwsza księga "Pana Tadeusza". Docelowo chciałbym, żeby długość tekstu nie miała znaczenia (program może wykonywać się i pół godziny, o to nie dbam; żeby tylko pamięci nie zabierał więcej niż, powiedzmy, 1 GB).