Usando Perl para contar o número de números científicos em um arquivo


10

Como posso contar o número de números científicos em um arquivo? O arquivo também possui algumas linhas de cabeçalho que precisam ser ignoradas.

Uma parte do conteúdo do arquivo está abaixo.

FileHeaderLine1
FileHeaderLine2
FileHeaderLine3
FileHeaderLine4
2.91999996E-001 2.97030300E-001 3.02060604E-001 3.07090908E-001 3.12121212E-001 3.17151517E-001
3.22181821E-001 3.27212125E-001 3.32242429E-001 3.37272733E-001 3.42303038E-001 3.47333342E-001
3.52363646E-001 3.57393950E-001 3.62424254E-001 3.67454559E-001 3.72484863E-001 3.77515137E-001
3.82545441E-001 3.87575746E-001 3.92606050E-001 3.97636354E-001 4.02666658E-001 4.07696962E-001
4.12727267E-001 4.17757571E-001 4.22787875E-001 4.27818179E-001 4.32848483E-001 4.37878788E-001
4.42909092E-001 4.47939396E-001 4.52969700E-001

Então, como posso pular as quatro primeiras linhas do exemplo acima e contar o número de números científicos no arquivo?

Respostas:


14

Com o módulo principal Scalar::Util, você pode fazer:

$ perl -MScalar::Util=looks_like_number -anle '
    $count += grep { looks_like_number($_) } @F;
    END { print $count }
' file
33

Mais informações looks_like_numberpodem ver em perldoc perlapi.


+1 legal, eu não sabia sobrelooks_like_number
steeldriver

7

Usando o GNU grep

Você pode grepfazer isso usando as instalações do PCRE. Aliás, o mesmo padrão também pode ser usado no Perl:

$ grep -oP '\d+E[-+]?\d+' file.txt  | wc -l
33

Você também pode usar wc -wpara contar palavras, estou contando as linhas acima, mas grepretorna uma única correspondência em uma linha, para que realmente não importe nesse cenário.

Usando Perl

Para Perl, você pode usar este liner:

$ perl -lane '$c += grep /\d+E[-+]?\d+/, @F; END { print $c; }' file.txt 
33

Referências


@StephaneChazelas - obrigado pela edição. Desculpe, eu só estou nos sistemas GNU, por isso tendem a esquecer esse ponto o tempo todo. Vou tentar não cometer esse erro.
slm

4

egrep irá funcionar:

egrep "[0-9].[0-9]E-[0-9]" YourFile | wc -w

ATUALIZAR:

se uma linha contiver um número e outra string, podemos usar awkpara resolver o problema:

awk -F' ' '{for(i=1;i<=NF;i++)if(!(i%1))$i=$i "\n"}1' YourFile | egrep "[0-9].[0-9]E-[0-9]" | wc -w ( or wc -l )

Isso daria resultados incorretos se uma linha contivesse um número e outra string. A resposta acima que usa a opção -o do grep para gerar apenas correspondências é mais correta.
Johnny

Eu não sabia sobre -oPopção mencionado na resposta SLM antes, mas eu fixo meu problema usando awk@Johnny
Nidal

3

Supondo que você tenha apenas números científicos após a quarta linha, você pode fazer algo como abaixo.

tail -n +5 filename | wc - w

Para a entrada que você forneceu, a saída é 33 depois de executar o comando acima.


3

Se você precisar simplesmente contar o número de campos delimitados por espaço em branco após as linhas de cabeçalho em perl, acho que você poderia simplesmente

perl -lane '$sum += $#F+1 if $. > 4; END{print $sum}' file

Se você realmente precisar contar apenas números formatados cientificamente, uma abordagem pode ser procurar e substituir números de acordo com uma regex adequada e contar o número de substituições (a expressão de substituição perl retorna o número de substituições quando você a vincula a uma variável )

perl -lane '$sum += s/[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?//g if $. > 4; END{print $sum}' file

2

Tudo se resume ao que você realmente deseja considerar um número científico , o que você pode esperar que sua entrada contenha e onde você pode aceitar encontrar esses números na entrada.

Por exemplo, em:

That's inferior to the LK2E2000 model.

Posso encontrar os números 0 ou 2 (inf e 2E2000) ou 3 (inf, 2E200, 0) (ou levados ao extremo, procurando todas as sequências de caracteres que formam um número válido: 17 (inf, 2, 2E2, 2E20, 2E200, 2E200, 2E2000, 2, 20, 200, 2000, 0, 00, 000, 0, 00, 0)).

Se você sabe que sua entrada possui apenas números no X.XXXXXXXXE-XXX, e que eles estão com suas próprias palavras, pode ser mais seguro procurar apenas isso em palavras inteiras como:

tr -s '[[:blank:]]' '[\n*]' | LC_ALL=C grep -xEc '[0-9]\.[0-9]{8}E-[0-9]{3}'

A idéia é obter uma palavra por linha e combinar a linha inteira ( -x) com o padrão desejado. Para permitir qualquer número de notação cientifico (-1,2e + 1234 ... contanto que haja um eou E), você pode alterar o padrão para:

[-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9])[eE][-+]?[0-9]+

Ou torne a e...peça opcional para permitir todos os tipos de números decimais de ponto flutuante:

[-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9])([eE][-+]?[0-9]+)?

Isso tudo dá a mesma resposta para sua entrada específica, mas onde isso faria diferença é onde há entrada que se afasta do padrão estrito mostrado em sua amostra.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.