Utilizzo di Perl per contare il numero di numeri scientifici in un file


10

Come posso contare il numero di numeri scientifici in un file? Il file ha anche alcune righe di intestazione che devono essere saltate.

Una parte del contenuto del file è in basso.

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

Quindi, come posso saltare le prime quattro righe dell'esempio sopra e contare il numero di numeri scientifici nel file?

Risposte:


14

Con il modulo principale Scalar::Util, puoi fare:

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

Maggiori informazioni su looks_like_numberpossono vedere in perldoc perlapi.


+1 bello, non lo sapevolooks_like_number
steeldriver,

7

Usando GNU grep

È possibile utilizzare grepper fare ciò, utilizzando le funzionalità PCRE. Per inciso lo stesso modello può essere utilizzato anche in Perl:

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

Puoi anche usare wc -wper contare le parole, sto contando le righe sopra, ma greprestituisce una singola corrispondenza su una riga, quindi non importa davvero in quello scenario.

Utilizzando Perl

Per Perl puoi usare questa fodera:

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

Riferimenti


@StephaneChazelas - grazie per la modifica. Mi dispiace di essere sempre e solo sui sistemi GNU, quindi tende a dimenticare questo punto continuamente. Proverò a non commettere questo errore.
slm

4

egrep funzionerà:

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

AGGIORNARE:

se una riga contiene sia un numero che un'altra stringa, possiamo usare awkper risolvere il 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 )

Ciò darebbe risultati errati se una riga contenesse sia un numero che un'altra stringa. La risposta sopra che utilizza l'opzione -o di grep per produrre solo corrispondenze è più corretta.
Johnny,

Non sapevo delle -oPopzioni menzionate nella risposta slm prima, ma ho risolto il mio problema usando awk@Johnny
Nidal il

3

Supponendo di avere solo numeri scientifici dopo la 4a riga, puoi fare qualcosa come sotto.

tail -n +5 filename | wc - w

Per l'input fornito, l'output è 33 dopo aver eseguito il comando precedente.


3

Se devi semplicemente contare il numero di campi delimitati da spazi bianchi seguendo le righe di intestazione in perl, penso che potresti semplicemente fare

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

Se hai davvero bisogno di contare solo numeri formattati scientificamente, un approccio potrebbe essere quello di cercare e sostituire i numeri in base a una regex adatta e quindi contare il numero di sostituzioni (l'espressione di sostituzione perl restituisce il numero di sostituzioni quando lo associ a una variabile )

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

2

Tutto si riduce a ciò che in realtà vuoi considerare un numero scientifico , a ciò che puoi aspettarti che il tuo input contenga e dove puoi accettare di trovare quei numeri nell'input.

Ad esempio, in:

That's inferior to the LK2E2000 model.

Posso trovare 0 o 2 (inf e 2E2000) o 3 (inf, 2E200, 0) numeri (o portati all'estremo, cercando tutte le sequenze di caratteri che formano un numero valido: 17 (inf, 2, 2E2, 2E20, 2E200, 2E200, 2E2000, 2, 20, 200, 2000, 0, 00, 000, 0, 00, 0)).

Se sai che il tuo input ha solo numeri in X.XXXXXXXXE-XXX e che sono scritti su parole proprie, potrebbe essere più sicuro cercarlo solo in parole intere come:

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

L'idea è quella di ottenere una parola per riga e far corrispondere l'intera riga ( -x) allo schema desiderato. Per consentire qualsiasi numero di notazione scientify (-1.2e + 1234 ... purché sia ​​presente un eo E), è possibile modificare il modello in:

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

Oppure rendere e...facoltativo il pezzo per consentire tutti i tipi di numeri decimali in virgola mobile:

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

Tutto ciò dà la stessa risposta per il tuo input specifico, ma dove ciò farebbe la differenza è dove c'è un input che si discosta dal modello rigoroso mostrato nel tuo esempio.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.