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 tem algumas linhas de cabeçalho que precisam ser puladas.

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?

    
por AFP 20.06.2014 / 02:23

6 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 sobre looks_like_number pode ver em perldoc perlapi .

    
por 20.06.2014 / 03:35
7

Usando o GNU grep

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

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

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

Usando o Perl

Para Perl, você poderia usar este forro:

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

Referências

por 20.06.2014 / 02:50
4

egrep funcionará:

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

ATUALIZAÇÃO:

se uma linha continha tanto um número quanto outra string, podemos usar awk para 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 )
    
por 20.06.2014 / 02:48
3

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

tail -n +5 filename | wc - w

Para a entrada que você forneceu, a saída é 33 após a execução do comando acima.

    
por 20.06.2014 / 02:37
3

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

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

Se você realmente precisa contar apenas números formatados cientificamente, uma abordagem pode ser pesquisar e substituir números de acordo com um regex adequado e, em seguida, contar o número de substituições (a expressão de substituição perl retorna o número de substituições quando você ligá-lo a uma variável)

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

Tudo se resume ao que você realmente quer 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.

Eu posso encontrar números 0 ou 2 (inf e 2E2000) ou 3 (inf, 2E200, 0) (ou levado ao extremo, procurando todas as seqüê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 tem apenas números no X.XXXXXXXXE-XXX, e que eles têm palavras próprias, pode ser mais seguro procurar apenas por palavras 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 igualar a linha inteira ( -x ) com o padrão que você deseja. Para permitir qualquer número de notação científica (-1.2e + 1234 ... desde que haja um e ou E ), você pode alterar o padrão para:

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

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

[-+]?([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á uma entrada que se afasta do padrão estrito mostrado em sua amostra.

    
por 20.06.2014 / 14:23

Tags