Ich habe einige TSV-Daten
ID Name Email
1 test [email protected]
321 stan [email protected]
Ich möchte dies in eine Liste von Hashes analysieren
@entities[0]<Name> eq "test";
@entities[1]<Email> eq "[email protected]";
Ich habe Probleme mit der Verwendung des Newline-Metazeichens, um die Kopfzeile von den Wertzeilen abzugrenzen. Meine Grammatikdefinition:
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);
Aber das kehrt zurück Nil
. Ich glaube, ich verstehe etwas Grundlegendes an Regexen in Raku falsch.
Nil
. Was das Feedback angeht, ist es ziemlich unfruchtbar, oder? Laden Sie zum Debuggen Commaide herunter, falls Sie dies noch nicht getan haben, und / oder lesen Sie Wie kann die Fehlerberichterstattung in Grammatiken verbessert werden? . Sie haben,Nil
weil Ihr Muster eine Backtracking-Semantik angenommen hat. Siehe meine Antwort dazu. Ich empfehle Ihnen, Backtracking zu vermeiden. Siehe dazu die Antwort von @ user0721090601. Informationen zur praktischen Anwendbarkeit und Geschwindigkeit finden Sie in der Antwort von JJ. Außerdem einführende allgemeine Antwort auf "Ich möchte X mit Raku analysieren. Kann jemand helfen?" .Antworten:
Wahrscheinlich ist die Hauptsache, die es abwirft, die
\s
Übereinstimmung mit dem horizontalen und vertikalen Raum. Verwenden Sie\h
und nur den vertikalen Raum\v
.Eine kleine Empfehlung, die ich aussprechen würde, ist zu vermeiden, die Zeilenumbrüche in den Token aufzunehmen. Möglicherweise möchten Sie auch die Wechseloperatoren verwenden
%
oder%%
, da sie für die Ausführung dieser Art von Arbeit ausgelegt sind:Das Ergebnis
Parser.parse($dat)
hierfür ist folgendes:Das zeigt uns, dass die Grammatik alles erfolgreich analysiert hat. Konzentrieren wir uns jedoch auf den zweiten Teil Ihrer Frage, der in einer Variablen für Sie verfügbar sein soll. Dazu müssen Sie eine Aktionsklasse angeben, die für dieses Projekt sehr einfach ist. Sie erstellen einfach eine Klasse, deren Methoden mit den Methoden Ihrer Grammatik übereinstimmen (obwohl sehr einfache Methoden wie
value
/header
, die neben der Stringifizierung keine spezielle Verarbeitung erfordern, ignoriert werden können). Es gibt einige kreativere / kompaktere Möglichkeiten, Ihre Verarbeitung zu handhaben, aber ich werde zur Veranschaulichung einen ziemlich rudimentären Ansatz wählen. Hier ist unsere Klasse:Jede Methode hat die Signatur,
($/)
die die Regex-Übereinstimmungsvariable ist. Fragen wir nun, welche Informationen wir von jedem Token wünschen. In der Kopfzeile wollen wir jeden der Kopfwerte in einer Reihe. Damit:Alle Token mit einem Quantor auf sie wird als behandelt werden
Positional
, so dass wir auch jedes einzelne Header Spiel zugreifen konnte mit$<header>[0]
,$<header>[1]
etc. Aber das sind Match - Objekte, so dass wir nur schnell sie stringify. Mit demmake
Befehl können andere Token auf diese speziellen Daten zugreifen, die wir erstellt haben.Unsere Wertzeile wird identisch aussehen, da
$<value>
uns die Token wichtig sind.Wenn wir zur letzten Methode kommen, wollen wir das Array mit Hashes erstellen.
Hier können Sie sehen, wie wir auf die Inhalte zugreifen, in denen wir verarbeitet haben,
headerRow()
undvalueRow()
: Sie verwenden die.made
Methode. Da es mehrere valueRows gibtmade
, müssen wir eine Map erstellen, um jeden ihrer Werte zu erhalten (dies ist eine Situation, in der ich dazu neige, meine Grammatik einfach<header><data>
in die Grammatik zu schreiben und die Daten als mehrere Zeilen zu definieren, aber das ist es einfach genug ist es nicht schlecht).Nachdem wir die Überschriften und Zeilen in zwei Arrays haben, müssen wir sie nur noch zu einem Array von Hashes machen, was wir in der
for
Schleife tun . Dasflat @x Z @y
interkolonisiert nur die Elemente und die Hash-Zuweisung macht, was wir meinen, aber es gibt andere Möglichkeiten, das Array in den gewünschten Hash zu bekommen.Sobald Sie fertig sind, haben Sie es einfach
make
und dann wird es inmade
der Analyse verfügbar sein :Es ist ziemlich üblich, diese in eine Methode wie zu wickeln
Auf diese Weise kann man einfach sagen
quelle
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 }
Sie müssten es natürlich zuerst instanziieren:actions(Actions.new)
.class Actions { has @!header; has %!entries … }
die valueRow-Einträge direkt hinzufügen, sodass Sie am Ende nur noch erhaltenmethod TOP ($!) { make %!entries }
. Aber das ist doch Raku und TIMTOWTDI :-)<valueRow>+ %% \n
(Zeilen erfassen, die durch Zeilenumbrüche begrenzt sind), aber nach dieser Logik<.ws>* %% <header>
wäre "Erfassung optional" Leerzeichen, das durch Nicht-Leerzeichen begrenzt ist ". Vermisse ich etwas<.ws>
erfasst nicht (<ws>
würde). Das OP stellte fest, dass das TSV-Format möglicherweise mit einem optionalen Leerzeichen beginnt. In der Realität wäre dies wahrscheinlich noch besser definiert, wenn ein Token mit Zeilenabstand als definiert\h*\n\h*
wäre, wodurch die valueRow logischer definiert werden könnte als<header> % <.ws>
%
oder%%
bezeichnet zu haben. Aber es ist der richtige Name. (Die Verwendung von für|
,||
und Vetter hat mir immer seltsam.). Ich hatte noch nie an diese "Rückwärts" -Technik gedacht. Aber es ist eine nette Redewendung, um Regexes zu schreiben, die einem wiederholten Muster mit einer Trennzeichen-Behauptung entsprechen, nicht nur zwischen Übereinstimmungen des Musters, sondern es auch an beiden Enden (mit%%
) oder am Anfang, aber nicht am Ende (mit%
) als, ähm, zuzulassen. Alternative zur am Ende aber nicht Startlogik vonrule
und:s
. Nett. :)TL; DR: Das tust du nicht. Verwenden Sie einfach
Text::CSV
, die mit jedem Format umgehen kann.Ich werde zeigen, wie alt
Text::CSV
wahrscheinlich nützlich sein wird:Der Schlüsselteil hier ist das Datenmunging, das die ursprüngliche Datei in ein Array oder Arrays (in
@data
) konvertiert . Es wird jedoch nur benötigt, weil dercsv
Befehl keine Zeichenfolgen verarbeiten kann. Wenn sich Daten in einer Datei befinden, können Sie loslegen.Die letzte Zeile wird gedruckt:
Das ID-Feld wird zum Schlüssel für den Hash und das Ganze zu einer Reihe von Hashes.
quelle
TL; DRs
regex
Backtrack.token
s nicht. Deshalb stimmt Ihr Muster nicht überein. Diese Antwort konzentriert sich darauf, dies zu erklären und wie Sie Ihre Grammatik trivial korrigieren können. Sie sollten es jedoch wahrscheinlich neu schreiben oder einen vorhandenen Parser verwenden. Dies sollten Sie auf jeden Fall tun, wenn Sie nur TSV analysieren möchten, anstatt mehr über Raku-Regexe zu erfahren.Ein grundlegendes Missverständnis?
(Wenn Sie bereits wissen, dass der Begriff "Regexes" sehr vieldeutig ist, sollten Sie diesen Abschnitt überspringen.)
Eine grundlegende Sache, die Sie möglicherweise missverstehen, ist die Bedeutung des Wortes "Regexes". Hier sind einige populäre Bedeutungen, die die Leute annehmen:
Formale reguläre Ausdrücke.
Perl Regexes.
Perl-kompatible reguläre Ausdrücke (PCRE).
Textmuster-Matching-Ausdrücke, die als "Regexes" bezeichnet werden und wie die oben genannten aussehen und etwas Ähnliches tun.
Keine dieser Bedeutungen ist miteinander kompatibel.
Während Perl-Regexe semantisch eine Obermenge formaler regulärer Ausdrücke sind, sind sie in vielerlei Hinsicht weitaus nützlicher, aber auch anfälliger für pathologisches Backtracking .
Während Perl-kompatible reguläre Ausdrücke in dem Sinne mit Perl kompatibel sind, dass sie ursprünglich mit Standard-Perl-Regexen Ende der 90er Jahre identisch waren, und in dem Sinne, dass Perl steckbare Regex-Engines einschließlich der PCRE-Engine unterstützt, ist die PCRE-Regex-Syntax nicht mit dem Standard identisch Perl-Regex wird von Perl im Jahr 2020 standardmäßig verwendet.
Und während Textmuster-Matching-Ausdrücke, die als "Regexes" bezeichnet werden, im Allgemeinen einander ähneln und alle mit Text übereinstimmen, gibt es Dutzende, vielleicht Hunderte von Variationen in der Syntax und sogar in der Semantik für dieselbe Syntax.
Raku-Textmuster-Übereinstimmungsausdrücke werden normalerweise entweder als "Regeln" oder als "reguläre Ausdrücke" bezeichnet. Die Verwendung des Begriffs "Regexes" vermittelt die Tatsache, dass sie etwas wie andere Regexes aussehen (obwohl die Syntax bereinigt wurde). Der Begriff "Regeln" vermittelt die Tatsache, dass sie Teil eines viel breiteren Satzes von Funktionen und Werkzeugen sind , die bis zum Parsen (und darüber hinaus) skaliert werden können.
Die schnelle Lösung
Mit dem oben genannten grundlegenden Aspekt des Wortes "Regexes" aus dem Weg kann ich mich nun dem grundlegenden Aspekt des Verhaltens Ihres "Regex" zuwenden .
Wenn wir drei der Muster in Ihrer Grammatik für den
token
Deklarator auf denregex
Deklarator umstellen , funktioniert Ihre Grammatik wie beabsichtigt:Der einzige Unterschied zwischen a
token
und aregex
besteht darin, dass aregex
zurückverfolgt, während atoken
dies nicht tut. Somit:Während der Verarbeitung des letzten Musters (das könnte und wird oft als "Regex" bezeichnet, dessen tatsächlicher Deklarator jedoch
token
nicht istregex
),\S
wird das Muster verschluckt'b'
, so wie es vorübergehend während der Verarbeitung des Regex in der vorherigen Zeile geschehen ist. Da das Muster jedoch als deklariert isttoken
, wird die Regelengine (auch als "Regex-Engine" bezeichnet) nicht zurückverfolgt , sodass die Gesamtübereinstimmung fehlschlägt.Das ist es, was in Ihrem OP vor sich geht.
Die richtige Lösung
Eine bessere Lösung im Allgemeinen besteht darin, sich von der Annahme eines Backtracking-Verhaltens zu entwöhnen , da es langsam und sogar katastrophal langsam sein kann (nicht vom hängenden Programm zu unterscheiden), wenn es beim Abgleich mit einer böswillig konstruierten Zeichenfolge oder einer Zeichenfolge mit einer versehentlich unglücklichen Kombination von Zeichen verwendet wird.
Manchmal sind
regex
s angemessen. Wenn Sie beispielsweise eine einmalige Ausgabe schreiben und eine Regex die Aufgabe übernimmt, sind Sie fertig. Das ist gut. Dies ist einer der Gründe, warum die/ ... /
Syntax in Raku genau wie ein Backtracking-Muster deklariertregex
. (Andererseits können Sie schreiben,/ :r ... /
wenn Sie das Ratschen einschalten möchten - "Ratsche" bedeutet das Gegenteil von "Backtrack", also:r
schaltet eine Regex auftoken
Semantik um.)Gelegentlich spielt das Zurückverfolgen in einem Analysekontext immer noch eine Rolle. Während die Grammatik für Raku im Allgemeinen das Zurückverfolgen meidet und stattdessen Hunderte von
rule
s undtoken
s hat, hat sie dennoch 3regex
s.Ich habe die Antwort von @ user0721090601 ++ positiv bewertet, weil sie nützlich ist. Es werden auch einige Dinge angesprochen, die mir in Ihrem Code sofort als idiomatisch erschienen, und vor allem bei
token
s bleiben . Es kann durchaus die Antwort sein, die Sie bevorzugen, die cool sein wird.quelle