Eu tenho um arquivo txt com blocos de texto como este:
17-01-2023
Purchase AAA
Apple Pay John Doe
Full Payment
-11,34€
0,11€
30-01-2023
Purchase BBB
Mastercard Jane Doe
Installment
-23,90€
0,24€
Assim, temos em sequência, a data, o tipo de compra, o tipo de pagamento e nome, o tipo de pagamento, o valor como negativo e o desconto.
Eles se repetem no arquivo com milhares de entradas.
Quero somar os valores, neste exemplo, 11,34 + 23,90 e obter o total como positivo. Lembre-se de que há um símbolo do euro após o número e que, na minha localidade, a vírgula é o separador decimal.
Como faço isso no terminal, usando, digamos, sed, awk, etc.?
Você pode usar awk - ao contrário do caso em um prefixo de símbolo de moeda não numérico como
€-23,90
, um sufixo não numérico será ignorado durante a conversão numérica. Esteja ciente de que diferentes implementações podem lidar com o separador decimal da localidade de maneira diferente, ex.respeita
LC_NUMERIC
/LC_ALL
conforme exigido para conformidade com POSIX , enquanto o GNU awk se desvia da especificação POSIX por padrão e precisa ser instruído a usar sua localidade:Veja o Guia do Usuário GNU Awk em Localidades Podem Influenciar a Conversão
Testando com a localidade de_DE.UTF-8, por exemplo:
e no Mac OS:
Supondo que o valor da transação esteja sempre na 5ª linha de um registro, um início de registro é indicado por uma data no formato "DD-MM-AAAA" e que esse padrão só pode ocorrer no início do registro, o seguinte
awk
o programa fará:Funciona da seguinte forma:
locale decimal_point
como variáveldpt
para o programa. Isso é relevante porque você parece estar usandoawk
em uma configuração onde a entrada é formatada usando,
como separador decimal, mas a localidade está definida para algo que usa.
, fazendo comawk
que perca a parte decimal do número.line_of_record
como 0.line_of_record
contador. Se chegar a 5, substituirá o,
por a.
(se necessário) para queawk
interprete o conteúdo da linha como número decimal e subtraia o conteúdo da linha de uma variáveltotal
para somar valores positivos de pagamento.Isso é mais código do que o mínimo necessário, mas torna o programa um pouco mais robusto se houver linhas vazias separando registros (ainda depende do valor da transação estar na 5ª linha de um registro).
Este aqui apenas por diversão - assume GNU sed para a
n~m
construção:(é claro que você pode adicionar outro sed ou tr para converter o ponto decimal de volta ao local original,
,
se desejar).Usando Raku (anteriormente conhecido como Perl_6)
Sem verificação de tipo:
Com verificação de tipo:
Resumidamente, o Raku é executado na linha de comando com os
-ne
sinalizadores de não impressão automática. Uma variável escalar$sum1
éstate
d, o que significa que ela será instanciada antes do início do loop direcionado pelos-ne
sinalizadores. Na segunda instrução, se uma variável de contador de linha incrementada anônima++$
quando%
o módulo dividido por 6 for igual a 5,trans
apague a,
vírgula para.
colocar um ponto e remover (subst
sem nada) o€
símbolo do Euro. Em seguida,+=
acumule na$sum
variável. No finalEND
do loop,say $sum1
.Exemplo de Entrada:
Saída de amostra:
Para um total acumulativo, apenas
say
a variável acumulativa:Saída de amostra:
Inspirado na resposta de @AdminBee
awk
, você pode tolerar linhas em branco entre os registros se incrementar o contador de linhas apenas quando as linhas contiverem.chars
caracteres:Saída de amostra (o mesmo que acima):
Observe que os números fornecidos no exemplo do OP são digitados como
Rat
números racionais por padrão no Raku (outros tipos disponíveis no Raku incluemNum
s eInt
s).Rat
s em Raku (supondo que sejam pequenos o suficiente) geralmente não sofrem de erros de arredondamento e são rapidamente convertidos em frações. Por exemplo, altere aEND
instrução da seguinte maneira:Saída de amostra:
Para uma manipulação mais rápida,
say $sum1.nude
retorna(-881 25)
.https://docs.raku.org/language/numerics.html#Rational
https://raku.org