J'ai des données TSV
ID Name Email
1 test [email protected]
321 stan [email protected]
Je voudrais analyser ceci dans une liste de hachages
@entities[0]<Name> eq "test";
@entities[1]<Email> eq "[email protected]";
J'ai du mal à utiliser le métacaractère de nouvelle ligne pour délimiter la ligne d'en-tête des lignes de valeur. Ma définition grammaticale:
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);
Mais cela revient Nil
. Je pense que je ne comprends pas quelque chose de fondamental à propos des regex dans le raku.
Nil
. C'est assez stérile en ce qui concerne les commentaires, non? Pour le débogage, téléchargez la virgule si vous ne l'avez pas déjà fait et / ou consultez Comment améliorer le rapport d'erreurs dans les grammaires? . Vous avez obtenuNil
parce que votre modèle supposait une sémantique de retour arrière. Voir ma réponse à ce sujet. Je vous recommande d'éviter le retour en arrière. Voir la réponse de @ user0721090601 à ce sujet. Pour la praticité et la vitesse, voir la réponse de JJ. Aussi, réponse générale d'introduction à "Je veux analyser X avec Raku. Quelqu'un peut-il aider?" .Réponses:
La principale chose qui le jette est probablement celle qui
\s
correspond à l'espace horizontal et vertical. Pour correspondre à l' espace horizontal juste, l' utilisation\h
, et pour correspondre à l' espace juste vertical,\v
.Une petite recommandation que je ferais est d'éviter d'inclure les nouvelles lignes dans le jeton. Vous pouvez également utiliser les opérateurs d'alternance
%
ou%%
, comme ils sont conçus pour gérer ce type de travail:Le résultat de
Parser.parse($dat)
ceci est le suivant:ce qui nous montre que la grammaire a tout analysé avec succès. Cependant, concentrons-nous sur la deuxième partie de votre question, que vous souhaitez qu'elle soit disponible dans une variable pour vous. Pour ce faire, vous devrez fournir une classe d'actions très simple pour ce projet. Vous créez simplement une classe dont les méthodes correspondent aux méthodes de votre grammaire (bien que des méthodes très simples, comme
value
/header
qui ne nécessitent pas de traitement spécial en plus de la stringification, puissent être ignorées). Il existe des moyens plus créatifs / compacts de gérer le traitement de la vôtre, mais je vais suivre une approche assez rudimentaire pour l'illustration. Voici notre classe:Chaque méthode a la signature
($/)
qui est la variable de correspondance d'expression régulière. Alors maintenant, demandons quelles informations nous voulons de chaque jeton. Dans la ligne d'en-tête, nous voulons chacune des valeurs d'en-tête, dans une ligne. Donc:Tout jeton avec un quantificateur sur elle sera traitée comme
Positional
, afin que nous puissions également accéder à chaque match d' en- tête individuel avec$<header>[0]
,$<header>[1]
etc. Mais ce sont des objets de match, alors on les stringify rapidement. Lamake
commande permet à d'autres jetons d'accéder à ces données spéciales que nous avons créées.Notre ligne de valeur sera identique, car les
$<value>
jetons sont ce dont nous nous soucions.Lorsque nous arriverons à la dernière méthode, nous voudrons créer le tableau avec des hachages.
Ici, vous pouvez voir comment nous accédons aux trucs que nous avons traités
headerRow()
etvalueRow()
: Vous utilisez la.made
méthode. Parce qu'il y a plusieurs ValueRows, pour obtenir chacune de leursmade
valeurs, nous devons faire une carte (c'est une situation où j'ai tendance à écrire ma grammaire pour avoir simplement<header><data>
dans la grammaire, et définir les données comme étant plusieurs lignes, mais c'est assez simple c'est pas trop mal).Maintenant que nous avons les en-têtes et les lignes dans deux tableaux, il s'agit simplement d'en faire un tableau de hachages, ce que nous faisons dans la
for
boucle. Leflat @x Z @y
juste interconnecte les éléments, et l'affectation de hachage fait ce que nous voulons dire, mais il existe d'autres façons d'obtenir le tableau dans le hachage souhaité.Une fois que vous avez terminé, vous venez de le
make
faire, puis il sera disponible dans lemade
de l'analyse:Il est assez courant de les intégrer dans une méthode, comme
De cette façon, vous pouvez simplement dire
la source
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 }
Vous devrez bien sûr l'instancier en premier:actions(Actions.new)
.class Actions { has @!header; has %!entries … }
et je demanderais simplement à valueRow d'ajouter directement les entrées pour que vous vous retrouviez avec justemethod TOP ($!) { make %!entries }
. Mais c'est Raku après tout et TIMTOWTDI :-)<valueRow>+ %% \n
(<.ws>* %% <header>
capturer des lignes délimitées par des sauts de ligne), mais en suivant cette logique, ce serait "capture facultative espaces blancs délimités par des espaces non blancs ". Suis-je en train de manquer quelque chose?<.ws>
ne capture pas (le<ws>
ferait). L'OP a noté que le format TSV peut commencer par un espace facultatif. En réalité, cela serait probablement encore mieux défini avec un jeton d'espacement de ligne défini comme\h*\n\h*
, ce qui permettrait à valueRow d'être défini plus logiquement comme<header> % <.ws>
%
/%%
appelé une "alternance" op auparavant. Mais c'est le bon nom. (Considérant que l' utilisation de celui - ci pour|
,||
et ses cousins m'a toujours frappé comme bizarre.). Je n'avais pas pensé à cette technique "à l'envers" auparavant. Mais c'est un joli idiome pour écrire des expressions rationnelles correspondant à un motif répété avec une assertion de séparateur non seulement entre les correspondances du motif mais aussi en le permettant aux deux extrémités (en utilisant%%
), ou au début mais pas en fin (en utilisant%
), comme un, euh, alternative à la logique de fin et non de début derule
et:s
. Agréable. :)TL; DR: vous ne le faites pas. Utilisez simplement
Text::CSV
, qui est capable de gérer tous les formats.Je vais montrer quel âge
Text::CSV
sera probablement utile:La partie clé ici est la fusion de données qui convertit le fichier initial en un ou plusieurs tableaux (en
@data
). Cependant, il n'est nécessaire que parce que lacsv
commande n'est pas en mesure de traiter les chaînes; si les données sont dans un fichier, vous êtes prêt à partir.La dernière ligne imprimera:
Le champ ID deviendra la clé du hachage, et le tout un tableau de hachages.
la source
Retour en
regex
arrière de TL; DR .token
s pas. C'est pourquoi votre motif ne correspond pas. Cette réponse se concentre sur l'explication de cela et sur la façon de corriger trivialement votre grammaire. Cependant, vous devriez probablement le réécrire, ou utiliser un analyseur existant, ce que vous devez absolument faire si vous voulez simplement analyser TSV plutôt que de vous renseigner sur les expressions rationnelles raku.Un malentendu fondamental?
(Si vous savez déjà que le terme "regexes" est très ambigu, pensez à sauter cette section.)
Une chose fondamentale que vous pourriez mal comprendre est le sens du mot «regexes». Voici quelques significations populaires que les gens supposent:
Expressions régulières formelles.
Perl regexes.
Expressions régulières compatibles Perl (PCRE).
Expressions de correspondance de modèle de texte appelées «expressions régulières» qui ressemblent à l'une des expressions ci-dessus et font quelque chose de similaire.
Aucune de ces significations n'est compatible les unes avec les autres.
Bien que les expressions rationnelles de Perl soient sémantiquement un surensemble d'expressions régulières formelles, elles sont beaucoup plus utiles à bien des égards, mais aussi plus vulnérables aux retours en arrière pathologiques .
Bien que les expressions régulières compatibles Perl soient compatibles avec Perl dans le sens où elles étaient à l' origine les mêmes que les expressions régulières Perl à la fin des années 1990, et dans le sens où Perl prend en charge les moteurs regex enfichables, y compris le moteur PCRE, la syntaxe des expressions régulières PCRE n'est pas identique à la norme Perl regex utilisé par défaut par Perl en 2020.
Et bien que les expressions d'appariement de motifs de texte appelées «expressions régulières» se ressemblent généralement un peu et correspondent toutes au texte, il existe des dizaines, voire des centaines, de variations de syntaxe, et même de sémantique pour la même syntaxe.
Les expressions de correspondance de motifs de texte Raku sont généralement appelées "règles" ou "expressions régulières". L'utilisation du terme «regexes» traduit le fait qu'ils ressemblent un peu à d'autres regexes (bien que la syntaxe ait été nettoyée). Le terme «règles» exprime le fait qu'elles font partie d'un ensemble beaucoup plus large de fonctionnalités et d'outils qui évoluent jusqu'à l'analyse (et au-delà).
La solution rapide
Avec l'aspect fondamental ci-dessus du mot «regexes», je peux maintenant passer à l'aspect fondamental du comportement de votre «regex» .
Si nous basculons trois des modèles de votre grammaire pour le
token
déclarant vers leregex
déclarant, votre grammaire fonctionne comme vous le vouliez:La seule différence entre a
token
et aregex
est qu'un a faitregex
marche arrière alors que cetoken
n'est pas le cas. Donc:Pendant le traitement du dernier motif (qui pourrait être et est souvent appelé "regex", mais dont le déclarant réel ne l'est
token
pasregex
), le\S
avalera le'b'
, tout comme il l'aura fait temporairement pendant le traitement du regex dans la ligne précédente. Mais, comme le modèle est déclaré en tant quetoken
, le moteur de règles (alias "moteur regex") ne revient pas en arrière , donc la correspondance globale échoue.C'est ce qui se passe dans votre PO.
La bonne solution
Une meilleure solution en général est de se sevrer de l' hypothèse d'un comportement de retour en arrière, car il peut être lent et même catastrophiquement lent (impossible à distinguer du blocage du programme) lorsqu'il est utilisé pour faire correspondre une chaîne construite de manière malveillante ou une chaîne avec une combinaison de caractères accidentellement malheureuse.
Parfois, les
regex
s sont appropriés. Par exemple, si vous écrivez une pièce unique et qu'une expression régulière fait le travail, alors vous avez terminé. C'est très bien. Cela fait partie de la raison pour laquelle la/ ... /
syntaxe dans raku déclare un modèle de retour arrière, tout commeregex
. (Là encore, vous pouvez écrire/ :r ... /
si vous souhaitez activer le cliquet - "cliquet" signifie l'opposé de "backtrack", donc:r
passe une expression régulière à latoken
sémantique.)Parfois, le retour en arrière a toujours un rôle dans un contexte d'analyse. Par exemple, alors que la grammaire du raku évite généralement le retour en arrière et contient plutôt des centaines de
rule
s ettoken
s, elle n'en a pas moins 3regex
s.J'ai voté pour la réponse de @ user0721090601 ++ parce qu'elle est utile. Il aborde également plusieurs choses qui m'ont immédiatement semblé être idiomatiquement éteintes dans votre code, et, surtout, s'en tient à l'
token
art. C'est peut-être la réponse que vous préférez, qui sera cool.la source