Mam trochę danych TSV
ID Name Email
1 test [email protected]
321 stan [email protected]
Chciałbym to parsować na listę skrótów
@entities[0]<Name> eq "test";
@entities[1]<Email> eq "[email protected]";
Mam problem z użyciem metaznaku nowej linii do oddzielenia wiersza nagłówka od wierszy wartości. Moja definicja gramatyki:
use v6;
grammar Parser {
token TOP { <headerRow><valueRow>+ }
token headerRow { [\s*<header>]+\n }
token header { \S+ }
token valueRow { [\s*<value>]+\n? }
token value { \S+ }
}
my $dat = q:to/EOF/;
ID Name Email
1 test [email protected]
321 stan [email protected]
EOF
say Parser.parse($dat);
Ale to wraca Nil
. Myślę, że nie rozumiem czegoś fundamentalnego w wyrażeniach regularnych w raku.
Nil
. Jeśli chodzi o opinie, to jest dość jałowe, prawda? W celu debugowania pobierz przecinek, jeśli jeszcze tego nie zrobiłeś i / lub zobacz Jak poprawić raportowanie błędów w gramatyce? . MaszNil
swój wzorzec zakładający semantykę cofania. Zobacz moją odpowiedź na ten temat. Polecam unikać powrotu. Zobacz odpowiedź @ user0721090601 na ten temat. Po prostu praktyczność i szybkość, patrz odpowiedź JJ. Również Wstępna ogólna odpowiedź na „Chcę przeanalizować X z Raku. Czy ktoś może pomóc?” .Odpowiedzi:
Prawdopodobnie najważniejszą rzeczą, która go rzuca, jest
\s
dopasowanie do przestrzeni poziomej i pionowej. Aby dopasować tylko poziomą przestrzeń, wykorzystanie\h
i dopasować tylko pionową przestrzeń,\v
.Jednym małym zaleceniem, które chciałbym zrobić, jest unikanie umieszczania nowego wiersza w tokenie. Możesz także użyć operatorów naprzemiennych
%
lub%%
, ponieważ są one zaprojektowane do obsługi tego typu pracy:Wynik
Parser.parse($dat)
tego jest następujący:co pokazuje nam, że gramatyka z powodzeniem przeanalizowała wszystko. Skupmy się jednak na drugiej części pytania, która ma być dostępna w zmiennej dla Ciebie. Aby to zrobić, musisz podać klasę działań, która jest bardzo prosta dla tego projektu. Po prostu tworzysz klasę, której metody pasują do metod twojej gramatyki (chociaż te bardzo proste, takie jak
value
/header
które nie wymagają specjalnego przetwarzania poza rygoryzacją, mogą zostać zignorowane). Istnieje kilka bardziej kreatywnych / kompaktowych sposobów radzenia sobie z przetwarzaniem, ale postaram się zastosować dość podstawowe podejście do ilustracji. Oto nasza klasa:Każda metoda ma sygnaturę,
($/)
która jest zmienną dopasowania wyrażenia regularnego. Więc teraz zapytajmy, jakie informacje chcemy od każdego tokena. W wierszu nagłówka chcemy każdej wartości nagłówka z rzędu. Więc:Dowolny znak z kwantyfikatorem na nim będą traktowane jako
Positional
, więc mogliśmy również dostęp do każdego indywidualnego meczu z nagłówka$<header>[0]
,$<header>[1]
itp Ale to są obiekty mecz, więc po prostu szybko stringify im.make
Komenda pozwala innym tokeny dostępu do tej szczególnej dane, które mamy utworzone.Nasz wiersz wartości będzie wyglądał identycznie, ponieważ
$<value>
dbamy o tokeny.Kiedy przejdziemy do ostatniej metody, będziemy chcieli utworzyć tablicę z skrótami.
Tutaj możesz zobaczyć, w jaki sposób uzyskujemy dostęp do rzeczy, w których przetwarzaliśmy
headerRow()
ivalueRow()
: Używasz.made
metody. Ponieważ istnieje wiele wierszy wartości, aby uzyskać każdą z ichmade
wartości, musimy zrobić mapę (jest to sytuacja, w której mam tendencję do pisania gramatyki po prostu<header><data>
w gramatyce i definiowania danych jako wielu wierszy, ale jest to dość proste, nie jest tak źle).Teraz, gdy mamy nagłówki i wiersze w dwóch tablicach, wystarczy po prostu zrobić z nich tablicę skrótów, co robimy w
for
pętli. Poflat @x Z @y
prostu interpoluje elementy, a przypisanie skrótu robi to, co mamy na myśli, ale istnieją inne sposoby uzyskania tablicy w haszu, którą chcesz.Gdy skończysz, po prostu
make
to, a następnie będzie dostępny wmade
parsowaniu:Dość powszechne jest zawijanie ich w metodę, taką jak
W ten sposób możesz po prostu powiedzieć
źródło
class Actions { has @!header; method headerRow ($/) { @!header = @<header>.map(~*); make @!header.List; }; method valueRow ($/) {make (@!header Z=> @<value>.map: ~*).Map}; method TOP ($/) { make @<valueRow>.map(*.made).List }
Oczywiście najpierw musisz to zrobić:actions(Actions.new)
.class Actions { has @!header; has %!entries … }
i po prostu miałbym wartośćRow dodaj wpisy bezpośrednio, abyś skończyłmethod TOP ($!) { make %!entries }
. Ale to w końcu Raku i TIMTOWTDI :-)<valueRow>+ %% \n
(przechwytywanie wierszy oddzielonych znakami nowej linii), ale zgodnie z tą logiką<.ws>* %% <header>
byłoby „przechwytywanie opcjonalne spacja, która jest oddzielona spacją ”. Czy coś brakuje?<.ws>
Nie uchwycić (<ws>
by). OP zauważył, że format TSV może zaczynać się od opcjonalnej białej spacji. W rzeczywistości byłoby to prawdopodobnie jeszcze lepiej zdefiniowane za pomocą tokena odstępu między wierszami zdefiniowanego jako\h*\n\h*
, co pozwoliłoby na bardziej logiczne zdefiniowanie<header> % <.ws>
%
/%%
określania jako „alternacja”. Ale to właściwa nazwa. (Podczas gdy użycie go|
,||
a kuzyni zawsze wydawali mi się dziwne.) Nie myślałem o tej „wstecznej” technice. Ale to fajny idiom do pisania wyrażeń regularnych pasujących do powtarzanego wzorca z pewną separacją nie tylko między dopasowaniami wzorca, ale także pozwalający na obu końcach (za pomocą%%
) lub na początku, ale nie na końcu (za pomocą%
), jako, er, alternatywa na końcu, ale nie logika początkowarule
i:s
. Miły. :)TL; DR: ty nie. Wystarczy użyć
Text::CSV
, który jest w stanie poradzić sobie z każdym formatem.Pokażę, ile lat
Text::CSV
będzie prawdopodobnie przydatne:Kluczową częścią tutaj jest munging danych, które przekształcają plik początkowy w tablicę lub tablice (in
@data
). Jest to potrzebne tylko dlatego, żecsv
polecenie nie jest w stanie poradzić sobie z łańcuchami; jeśli dane są w pliku, możesz iść.Ostatni wiersz zostanie wydrukowany:
Pole ID stanie się kluczem do skrótu, a całość będzie tablicą skrótów.
źródło
regex
Backtrack TL; DR .token
nie. Dlatego twój wzór nie pasuje. Ta odpowiedź skupia się na wyjaśnieniu tego i jak w prosty sposób naprawić gramatykę. Jednak prawdopodobnie powinieneś go przepisać lub użyć istniejącego parsera, co zdecydowanie powinieneś zrobić, jeśli chcesz po prostu parsować TSV, zamiast uczyć się o wyrażeniach regularnych raku.Podstawowe nieporozumienie?
(Jeśli znasz już termin „wyrażenia regularne” jest bardzo niejednoznaczny, rozważ pominięcie tej sekcji).
Jedną z podstawowych rzeczy, które mogą być nieporozumieniem, jest znaczenie słowa „wyrażenia regularne”. Oto kilka popularnych znaczeń, które lud zakłada:
Formalne wyrażenia regularne.
Perl wyrażenia regularne.
Wyrażenia regularne zgodne z Perlem (PCRE).
Wyrażenia pasujące do wzorca tekstowego zwane „wyrażeniami regularnymi”, które wyglądają jak wyżej, i robią coś podobnego.
Żadne z tych znaczeń nie jest ze sobą kompatybilne.
Chociaż wyrażenia regularne Perla są semantycznie nadzbiorem formalnych wyrażeń regularnych, są one o wiele bardziej przydatne na wiele sposobów, ale także bardziej podatne na patologiczne cofanie się .
Podczas gdy wyrażenia regularne zgodne z Perlem są kompatybilne z Perlem w tym sensie, że pierwotnie były takie same jak standardowe wyrażenia regularne Perla pod koniec lat 90. XX wieku oraz że Perl obsługuje wtykowe silniki wyrażeń regularnych , w tym silnik PCRE, składnia wyrażeń regularnych PCRE nie jest identyczna ze standardowym Wyrażenie regularne Perla używane domyślnie przez Perla w 2020 roku.
I chociaż wyrażenia pasujące do wzorca tekstowego zwane „wyrażeniami regularnymi” ogólnie wyglądają trochę podobnie do siebie i wszystkie pasują do tekstu, istnieją dziesiątki, być może setki odmian składni, a nawet semantyki dla tej samej składni.
Wyrażenia pasujące do wzorca tekstowego Raku są zwykle nazywane „regułami” lub „wyrażeniami regularnymi”. Użycie terminu „wyrażenia regularne” oznacza, że wyglądają one podobnie jak inne wyrażenia regularne (chociaż składnia została oczyszczona). Termin „reguły” oznacza fakt, że są one częścią znacznie szerszego zestawu funkcji i narzędzi, które można skalować do analizy (i nie tylko).
Szybka poprawka
Z powyższej zasadniczej aspekcie słowo „regexes” out of the way, mogę teraz przejść do zasadniczego aspektu „regex” 's zachowanie .
Jeśli zmienimy trzy wzorce w gramatyce
token
deklaratora naregex
deklarator, gramatyka będzie działać zgodnie z zamierzeniami:Jedyna różnica między a
token
i aregex
polega na tym, żeregex
ścieżki powrotnetoken
nie występują. A zatem:Podczas przetwarzania ostatniego wzorca (który może być i często nazywany jest „regex”, ale którego faktycznym deklaratorem
token
nie jestregex
),\S
połknie go'b'
, tak jak tymczasowo to zrobi podczas przetwarzania regex w poprzednim wierszu. Ponieważ wzorzec jest zadeklarowany jako atoken
, silnik reguł (inaczej „silnik wyrażenia regularnego”) nie cofa się , więc ogólne dopasowanie kończy się niepowodzeniem.Tak dzieje się w twojej OP.
Właściwa poprawka
Ogólnie lepszym rozwiązaniem jest odejście od zakładania zachowania wstecznego, ponieważ może być ono powolne, a nawet katastrofalnie wolne (nie do odróżnienia od zawieszenia programu), gdy jest używane do dopasowywania do złośliwie skonstruowanego łańcucha lub łańcucha z przypadkowo niefortunną kombinacją znaków.
Czasami
regex
s są odpowiednie. Na przykład, jeśli piszesz jednorazowo, a wyrażenie regularne wykonuje zadanie, to gotowe. W porządku. Jest to jeden z powodów, dla których/ ... /
składnia w raku deklaruje wzorzec cofania, podobnie jakregex
. (Potem znowu można pisać/ :r ... /
, jeśli chcesz, aby przełączyć się na wzmaganie - „zapadkowy” oznacza przeciwieństwo „BackTrack”, więc:r
włącza regex dotoken
semantyki).Czasami cofanie nadal odgrywa rolę w kontekście analizowania. Na przykład, podczas gdy gramatyka dla Raku generalnie unika Backtracking, a zamiast tego ma setki
rule
S itoken
S, to jednak nadal ma 3regex
s.Poprosiłem o odpowiedź @ user0721090601 ++, ponieważ jest przydatna. Odnosi się również do kilku rzeczy, które od razu wydawały mi się idiomatycznie wyłączone w twoim kodzie i, co ważne, trzymają się
token
s. Może to być odpowiedź, którą wolisz, która będzie fajna.źródło