Jak przechwytywać 0-2 grupy w wyrażeniach regularnych C ++ i drukować je?
Poszedłem do niestandardowego podejścia parsowania good'ol, gdy utknąłem w wyrażeniu regularnym. Nie okazało się to takie złe, ponieważ zawartość pliku można porządkować tokenem, a tokeny można analizować w pętli za pomocąbardzo prosta maszyna stanu. Ci, którzy chcą sprawdzić, jest fragment kodu, który robi to za pomocą range-for, iteratorów ifstream i niestandardowego tokenizera strumienia w moim drugim pytaniu w Stackoverflowtutaj. Techniki te znacznie zmniejszają złożoność niestandardowego analizatora składni.
Chciałbym tokenizować zawartość pliku w pierwszej części w grupach przechwytywania dwóch, a następnie po prostu wiersz po wierszu. Mam rozwiązanie półfunkcjonalne, ale chciałbym się nauczyć, jak to poprawić. Oznacza to, że bez „dodatkowego przetwarzania” uzupełnienia braku wiedzy w grupach przechwytywania. Następnie kilka wstępnych i na koniec dokładniejsze pytanie (linia
const std::regex expression("([^:]+?)(^:|$)");
... chciałbym zapytać o to w połączeniu z przetwarzaniem wyników tego).
Pliki, które są w zasadzie tak zdefiniowane:
definition_literal : value_literal
definition_literal : value_literal
definition_literal : value_literal
definition_literal : value_literal
HOW TO INTERPRET THE FOLLOWING SECTION OF ROWS
[DATA ROW 1]
[DATA ROW 2]
...
[DATA ROW n]
Gdzie każdy z wierszy danych składa się z pewnej liczby liczb całkowitych lub liczb zmiennoprzecinkowych oddzielonych białymi znakami. Każdy wiersz ma tyle liczb, ile inne (np. Każdy wiersz może mieć cztery liczby całkowite). Tak więc „sekcja interpretacji” zasadniczo informuje ten format w zwykłym tekście w jednym wierszu.
Mam prawie działające rozwiązanie, które czyta takie pliki w następujący sposób:
int main()
{
std::ifstream file("xyz", std::ios_base::in);
if(file.good())
{
std::stringstream file_memory_buffer;
file_memory_buffer << file.rdbuf();
std::string str = file_memory_buffer.str();
file.close();
const std::regex expression("([^:]+?)(^:|$)");
std::smatch result;
const std::sregex_token_iterator end;
for(std::sregex_token_iterator i(str.begin(), str.end(), expression); i != end; ++i)
{
std::cout << (*i) << std::endl;
}
}
return EXIT_SUCCESS;
}
Z zdefiniowanym wyrażeniem regularnymexpression
, teraz drukuje<value>
części pliku definicji, następnie część interpretacji, a następnie wiersze danych jeden po drugim. Jeśli zmienię wyrażenie regularne na
"([^:]+?)(:|$)"
... wypisuje wszystkie linie tokenowane w grupach po jednym, prawie jak chciałbym, ale jak tokenizować pierwszą część w grupach po dwie, a resztę po linii?
Wszelkie wskazówki, kod, wyjaśnienia są naprawdę mile widziane. Dzięki.
EDYTOWAĆ:Jak wspomnianoTom Kerr już, ale kilka dodatkowych punktów, to także próba lub kodowanie kata, jeśli nie, aby nie pisać niestandardowego parsera, ale żeby zobaczyć, czy mógłbym - lub moglibyśmy :-) - wykonać to za pomocą wyrażenia regularnego. Wiem, że wyrażenie regularne nie jest tutaj najbardziej wydajne, ale to nie ma znaczenia.
Mam nadzieję, że będzie to lista krotek informacji nagłówka (krotka o rozmiarze 2), a następnie linia INTERPRET (krotka o rozmiarze 1), której mogę użyć, aby wybrać funkcję dotyczącą tego, co zrobić z danymi linie (krotka o rozmiarze 1).
Tak, wiersz „JAK WYRAŻAĆ INTERPRETĘ” jest zawarty w zestawie dobrze zdefiniowanych ciągów i mógłbym po prostu odczytać wiersz po wierszu od początku, dzieląc ciągi po drodze, aż jedna z linii INTERPRETU zostanie spełniona. Wiem, że to rozwiązanie regex nie jest najskuteczniejszą metodą, ale bardziej przypomina kodowanie katy, aby móc napisać coś innego niż parsery klientów (i to już trochę czasu, kiedy pisałem w C ++ po raz ostatni, więc to też jest próba inaczej ).
EDYCJA 2Udało mi się uzyskać dostęp do krotek (w kontekście tego pytania), zmieniając typ iteratora, tak
const std::sregex_iterator end;
for(std::sregex_iterator i(str.begin(), str.end(), expression); i != end; ++i)
{
std::cout << "0: " << (*i)[0] << std::endl;
std::cout << "1: " << (*i)[1] << std::endl;
std::cout << "2: " << (*i)[2] << std::endl;
std::cout << "***" << std::endl;
}
Chociaż jest to nadal daleko od tego, co chciałbym mieć, jest coś nie tak z wyrażeniem regularnym, którego próbuję użyć. W każdym razie to nowe znalezisko, inny rodzaj iteratora również pomaga.