Eu tenho alguns dados TSV
ID Name Email
1 test [email protected]
321 stan [email protected]
Eu gostaria de analisar isso em uma lista de hashes
@entities[0]<Name> eq "test";
@entities[1]<Email> eq "[email protected]";
Estou tendo problemas com o uso do metacaractere de nova linha para delimitar a linha do cabeçalho das linhas de valor. Minha definição gramatical:
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);
Mas isso está voltando Nil
. Acho que estou entendendo algo fundamental sobre as expressões regulares no raku.
Nil
. É muito estéril no que diz respeito ao feedback, certo? Para depuração, faça o download commaide se você ainda não o fez e / ou consulte Como é possível melhorar o relatório de erros nas gramáticas? . Você assumiuNil
porque seu padrão assumiu a semântica de retrocesso. Veja minha resposta sobre isso. Eu recomendo que você evite voltar atrás. Veja a resposta de @ user0721090601 sobre isso. Para pura praticidade e velocidade, consulte a resposta de JJ. Além disso, a resposta geral introdutória a "Quero analisar X com Raku. Alguém pode ajudar?" .Respostas:
Provavelmente a principal coisa que está jogando fora é que
\s
coincide com o espaço horizontal e vertical. Para corresponder apenas ao espaço horizontal, use\h
e para corresponder apenas ao espaço vertical\v
,.Uma pequena recomendação que eu faria é evitar incluir as novas linhas no token. Você também pode usar os operadores de alternância
%
ou%%
, como eles foram projetados para lidar com este tipo de trabalho:O resultado
Parser.parse($dat)
disso é o seguinte:o que mostra que a gramática analisou tudo com êxito. No entanto, vamos nos concentrar na segunda parte da sua pergunta, que você deseja que ela esteja disponível em uma variável para você. Para fazer isso, você precisará fornecer uma classe de ações que seja muito simples para este projeto. Você acabou de criar uma classe cujos métodos correspondem aos métodos da sua gramática (embora os muito simples, como
value
/header
que não exijam processamento especial além da stringificação, possam ser ignorados). Existem algumas maneiras mais criativas / compactas de lidar com o processamento, mas seguirei com uma abordagem bastante rudimentar para ilustração. Aqui está a nossa turma:Cada método possui a assinatura
($/)
que é a variável de correspondência de regex. Então agora, vamos perguntar quais informações queremos de cada token. Na linha do cabeçalho, queremos cada um dos valores do cabeçalho, em uma linha. Assim:Qualquer token com um quantificador sobre ele será tratado como um
Positional
, por isso também pode acessar cada partida cabeçalho indivíduo com$<header>[0]
,$<header>[1]
etc. Mas esses são objetos jogo, então nós rapidamente stringify eles. Omake
comando permite que outros tokens acessem esses dados especiais que criamos.Nossa linha de valor será idêntica, porque os
$<value>
tokens são o que nos interessa.Quando chegarmos ao último método, queremos criar a matriz com hashes.
Aqui você pode ver como acessamos as coisas em que processamos
headerRow()
evalueRow()
: Você usa o.made
método Como existem várias valueRows, para obter cada um de seusmade
valores, precisamos fazer um mapa (essa é uma situação em que costumo escrever minha gramática simplesmente<header><data>
na gramática e defino os dados como sendo várias linhas, mas isso é simples o suficiente, não é tão ruim).Agora que temos os cabeçalhos e as linhas em duas matrizes, é simplesmente uma questão de torná-las uma matriz de hashes, o que fazemos no
for
loop. Oflat @x Z @y
just intercolates os elementos, e a atribuição de hash faz o que queremos dizer, mas existem outras maneiras de obter a matriz no hash desejado.Quando terminar, você apenas
make
o fará e estará disponível namade
análise:É bastante comum agrupá-los em um método, como
Dessa forma, você pode apenas dizer
fonte
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 }
É claro que você teria que instanciar primeiro:actions(Actions.new)
.class Actions { has @!header; has %!entries … }
e apenas o valueRow adicionaria as entradas diretamente para que você acabasse com apenasmethod TOP ($!) { make %!entries }
. Mas este é Raku afinal e TIMTOWTDI :-)<valueRow>+ %% \n
(Capturar linhas delimitadas por novas linhas), mas seguir essa lógica<.ws>* %% <header>
seria "capturar opcional espaço em branco delimitado por não-espaço em branco ". Estou esquecendo de algo?<.ws>
não captura (<ws>
seria). O OP observou que o formato TSV pode começar com um espaço em branco opcional. Na realidade, isso provavelmente seria ainda melhor definido com um token de espaçamento definido como\h*\n\h*
, o que permitiria que o valueRow fosse definido mais logicamente como<header> % <.ws>
%
/%%
denominado uma opção de "alternação" antes. Mas é o nome certo. (Considerando que a utilização do mesmo para|
,||
e primos sempre me pareceu estranho.). Eu nunca tinha pensado nessa técnica "inversa" antes. Mas é um bom idioma para escrever expressões regulares que correspondam a um padrão repetido com alguma afirmação separadora, não apenas entre correspondências do padrão, mas também permitindo nas duas extremidades (usando%%
) ou no início, mas não no final (usando%
), como um, er, alternativa ao no final, mas não iniciar a lógica derule
e:s
. Agradável. :)TL; DR: você não. Basta usar
Text::CSV
, que é capaz de lidar com todos os formatos.Vou mostrar quantos anos
Text::CSV
provavelmente será útil:A parte principal aqui é a transferência de dados que converte o arquivo inicial em uma matriz ou matrizes (pol
@data
). Só é necessário, no entanto, porque ocsv
comando não é capaz de lidar com strings; se os dados estiverem em um arquivo, você estará pronto.A última linha será impressa:
O campo ID se tornará a chave do hash, e a coisa toda será uma matriz de hashes.
fonte
Backtrack de TL; DR
regex
.token
s não. É por isso que seu padrão não corresponde. Esta resposta se concentra em explicar isso e em como corrigir sua gramática trivialmente. No entanto, você provavelmente deve reescrevê-lo ou usar um analisador existente, que é o que você definitivamente deve fazer se quiser apenas analisar o TSV em vez de aprender sobre as expressões regulares raku.Um mal-entendido fundamental?
(Se você já sabe que o termo "regexes" é altamente ambíguo, pule esta seção.)
Uma coisa fundamental que você pode estar entendendo mal é o significado da palavra "regexes". Aqui estão alguns significados populares que as pessoas assumem:
Expressões regulares formais.
Regexes Perl.
Expressões regulares compatíveis com Perl (PCRE).
Expressões de correspondência de padrão de texto chamadas "regexes" que se parecem com qualquer uma das opções acima e fazem algo semelhante.
Nenhum desses significados é compatível um com o outro.
Embora as expressões regulares Perl sejam semanticamente um superconjunto de expressões regulares formais, elas são muito mais úteis de várias maneiras, mas também mais vulneráveis ao retrocesso patológico .
Embora as expressões regulares compatíveis com Perl sejam compatíveis com Perl no sentido em que eram originalmente iguais às regexes padrão do Perl no final dos anos 90, e no sentido de que o Perl suporta mecanismos regex conectáveis, incluindo o mecanismo PCRE, a sintaxe do regex PCRE não é idêntica ao padrão Regex Perl usado por padrão pelo Perl em 2020.
E, embora as expressões correspondentes ao padrão de texto chamadas "regexes" geralmente se pareçam um com o outro e correspondam ao texto, existem dezenas, talvez centenas, de variações na sintaxe e até na semântica para a mesma sintaxe.
As expressões correspondentes de padrão de texto Raku são normalmente chamadas de "regras" ou "regexes". O uso do termo "regexes" transmite o fato de que eles se parecem com outros regexes (embora a sintaxe tenha sido limpa). O termo "regras" transmite o fato de que elas fazem parte de um conjunto muito mais amplo de recursos e ferramentas que se adaptam à análise (e além).
A solução rápida
Com o aspecto fundamental acima da palavra "regexes" fora do caminho, agora posso voltar ao aspecto fundamental do comportamento do seu "regex" .
Se alternarmos três dos padrões em sua gramática do
token
declarador para oregex
declarador, sua gramática funcionará como você deseja:A única diferença entre a
token
e aregex
é que umregex
recua enquanto que umtoken
não. Portanto:Durante o processamento do último padrão (que pode ser e costuma ser chamado de "regex", mas cujo declarador real
token
não éregex
), o elemento\S
será engolido'b'
, exatamente como o fez temporariamente durante o processamento do regex na linha anterior. Mas, como o padrão é declarado comotoken
, o mecanismo de regras (também conhecido como "mecanismo de expressão regular") não retorna , portanto a correspondência geral falha.É isso que está acontecendo no seu OP.
A correção certa
Uma solução melhor em geral é afastar-se de assumir um comportamento de retorno, porque pode ser lento e até catastroficamente lento (indistinguível da interrupção do programa) quando usado na correspondência com uma string mal-intencionada ou com uma combinação acidentalmente infeliz de caracteres.
Às vezes,
regex
s são apropriados. Por exemplo, se você estiver escrevendo um registro único e uma regex fizer o trabalho, estará pronto. Isso é bom. Isso é parte da razão pela qual a/ ... /
sintaxe no raku declara um padrão de retorno, assim comoregex
. (Então, novamente, você pode escrever/ :r ... /
se desejar ativar a catraca - "catraca" significa o oposto de "retorno", portanto,:r
alterna um regex paratoken
semântica.)Ocasionalmente, o retorno ainda desempenha um papel em um contexto de análise. Por exemplo, enquanto que a gramática para raku geralmente evita o retrocesso, e em vez disso tem centenas de
rule
s etoken
s, no entanto ele ainda tem 3regex
s.Promovi a resposta a @ user0721090601 ++ porque é útil. Ele também aborda várias coisas que imediatamente me pareciam estar linguisticamente fora do seu código e, o que é mais importante, adere a
token
s. Pode ser a resposta que você preferir, que será legal.fonte