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:
grammar Parser {
token TOP {
<headerRow> \n
<valueRow>+ %% \n
}
token headerRow { <.ws>* %% <header> }
token valueRow { <.ws>* %% <value> }
token header { \S+ }
token value { \S+ }
token ws { \h* }
}
Das Ergebnis Parser.parse($dat)
hierfür ist folgendes:
「ID Name Email
1 test test@email.com
321 stan stan@nowhere.net
」
headerRow => 「ID Name Email」
header => 「ID」
header => 「Name」
header => 「Email」
valueRow => 「 1 test test@email.com」
value => 「1」
value => 「test」
value => 「test@email.com」
valueRow => 「 321 stan stan@nowhere.net」
value => 「321」
value => 「stan」
value => 「stan@nowhere.net」
valueRow => 「」
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:
class ParserActions {
method headerRow ($/) { ... }
method valueRow ($/) { ... }
method TOP ($/) { ... }
}
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:
method headerRow ($/) {
my @headers = $<header>.map: *.Str
make @headers;
}
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 dem make
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.
method valueRow ($/) {
my @values = $<value>.map: *.Str
make @values;
}
Wenn wir zur letzten Methode kommen, wollen wir das Array mit Hashes erstellen.
method TOP ($/) {
my @entries;
my @headers = $<headerRow>.made;
my @rows = $<valueRow>.map: *.made;
for @rows -> @values {
my %entry = flat @headers Z @values;
@entries.push: %entry;
}
make @entries;
}
Hier können Sie sehen, wie wir auf die Inhalte zugreifen, in denen wir verarbeitet haben, headerRow()
und valueRow()
: Sie verwenden die .made
Methode. Da es mehrere valueRows gibt made
, 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 . Das flat @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 in made
der Analyse verfügbar sein :
say Parser.parse($dat, :actions(ParserActions)).made
-> [{Email => test@email.com, ID => 1, Name => test} {Email => stan@nowhere.net, ID => 321, Name => stan} {}]
Es ist ziemlich üblich, diese in eine Methode wie zu wickeln
sub parse-tsv($tsv) {
return Parser.parse($tsv, :actions(ParserActions)).made
}
Auf diese Weise kann man einfach sagen
my @entries = parse-tsv($dat);
say @entries[0]<Name>; # test
say @entries[1]<Email>; # stan@nowhere.net
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?" .