Utiliser Perl pour compter le nombre de nombres scientifiques dans un fichier


10

Comment puis-je compter le nombre de numéros scientifiques dans un fichier? Le fichier contient également quelques lignes d'en-tête qui doivent être ignorées.

Une partie du contenu du fichier se trouve ci-dessous.

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

Alors, comment puis-je ignorer les quatre premières lignes de l'exemple ci-dessus et compter le nombre de nombres scientifiques dans le fichier?

Réponses:


14

Avec le module de base Scalar::Util, vous pouvez faire:

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

Plus d'informations sur looks_like_numberpeuvent voir dans perldoc perlapi.


+1 cool, je ne savais paslooks_like_number
steeldriver

7

Utiliser GNU grep

Vous pouvez utiliser greppour ce faire, en utilisant les fonctionnalités PCRE. Par ailleurs, le même modèle peut également être utilisé en Perl:

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

Vous pouvez également utiliser wc -wpour compter les mots, je compte les lignes ci-dessus, mais le grepretourne une seule correspondance sur une ligne, donc cela n'a pas vraiment d'importance dans ce scénario.

Utiliser Perl

Pour Perl, vous pouvez utiliser cette doublure:

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

Références


@StephaneChazelas - merci pour la modification. Désolé, je ne suis que sur des systèmes GNU, alors j'ai tendance à oublier ce point tout le temps. Je vais essayer de ne pas faire cette erreur.
slm

4

egrep marchera:

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

METTRE À JOUR:

si une ligne contenait à la fois un nombre et une autre chaîne, nous pouvons utiliser awkpour résoudre le problème:

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 )

Cela donnerait des résultats incorrects si une ligne contenait à la fois un nombre et une autre chaîne. La réponse ci-dessus qui utilise l'option -o de grep pour afficher uniquement les correspondances est plus correcte.
Johnny

Je ne connaissais pas l' -oPoption mentionnée dans la réponse slm auparavant, mais j'ai résolu mon problème en utilisant awk@Johnny
Nidal

3

En supposant que vous n'avez que des chiffres scientifiques après la 4e ligne, vous pouvez faire quelque chose comme ci-dessous.

tail -n +5 filename | wc - w

Pour l'entrée que vous avez fournie, la sortie est 33 après l'exécution de la commande ci-dessus.


3

Si vous avez simplement besoin de compter le nombre de champs délimités par des espaces suivant les lignes d'en-tête en perl, je pense que vous pourriez simplement faire

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

Si vous avez vraiment besoin de compter uniquement des nombres au format scientifique, une approche pourrait être de rechercher et de remplacer des nombres selon une expression rationnelle appropriée , puis de compter le nombre de remplacements (l'expression de substitution perl renvoie le nombre de remplacements lorsque vous le liez à une variable )

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

2

Tout dépend de ce que vous voulez réellement considérer comme un numéro scientifique , de ce que vous pouvez vous attendre à ce que votre entrée contienne et où vous pouvez accepter de trouver ces nombres dans l'entrée.

Par exemple, dans:

That's inferior to the LK2E2000 model.

Je peux trouver 0 ou 2 (inf et 2E2000) ou 3 (inf, 2E200, 0) nombres (ou poussés à l'extrême, en recherchant toutes les séquences de caractères qui forment un nombre valide: 17 (inf, 2, 2E2, 2E20, 2E200, 2E200, 2E2000, 2, 20, 200, 2000, 0, 00, 000, 0, 00, 0)).

Si vous savez que votre entrée ne contient que des chiffres dans le X.XXXXXXXXE-XXX et qu'ils sont sur des mots qui leur sont propres, il peut être plus sûr de chercher juste cela dans des mots entiers comme:

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

L'idée est d'obtenir un mot par ligne et de faire correspondre la ligne entière ( -x) avec le motif souhaité. Pour autoriser n'importe quel numéro de notation scientify (-1.2e + 1234 ... tant qu'il y a un eou E), vous pouvez changer le modèle en:

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

Ou rendez la e...partie facultative pour autoriser toutes sortes de nombres décimaux à virgule flottante:

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

Tout cela donne la même réponse pour votre entrée spécifique, mais là où cela ferait une différence, c'est là où il y a une entrée qui s'écarte du modèle strict montré dans votre échantillon.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.