Comment collecter des statistiques d'occurrence d'octets dans un fichier binaire?

12

J'aimerais savoir l'équivalent de

cat inputfile | sed 's/\(.\)/\1\n/g' | sort | uniq -c

présenté dans /programming/4174113/how-to-gather-characters-usage-statistics-in-text-file-using-unix-commands pour la production de statistiques d'utilisation des caractères dans les fichiers texte pour le comptage des fichiers binaires octets simples au lieu de caractères, c'est-à-dire que la sortie doit être sous la forme de

18383 57
12543 44
11555 127
 8393 0

Peu importe si la commande prend autant de temps que celle référencée pour les caractères.

Si j'applique la commande de caractères aux fichiers binaires, la sortie contient des statistiques pour de longues séquences arbitraires de caractères non imprimables (je ne cherche pas d'explication à cela).

Karl Richter
la source

Réponses:

8

Avec GNU od:

od -vtu1 -An -w1 my.file | sort -n | uniq -c

Ou plus efficacement avec perl(génère également un nombre (0) pour les octets qui ne se produisent pas):

perl -ne 'BEGIN{$/ = \4096};
          $c[$_]++ for unpack("C*");
          END{for ($i=0;$i<256;$i++) {
              printf "%3d: %d\n", $i, $c[$i]}}' my.file
Stéphane Chazelas
la source
Pour que les numéros de la première ligne soient reconnus correctement, j'ai dû ajouter | sort -net | sort -n -rpour l'ordre décroissant respectivement (le tri ne faisait pas partie de la question). Le tri pourrait être mieux fait ...
Karl Richter
Semble un peu exagéré d'avoir à trier le fichier entier, mais ça a bien fonctionné pour moi.
Michael Anderson
Bon point @Karl, bien que non demandé, l'utilisation sort -nici a beaucoup plus de sens. Réponse mise à jour.
Stéphane Chazelas
4

Pour les fichiers volumineux, le tri sera lent. J'ai écrit un petit programme C pour résoudre le problème équivalent ( voir cet essentiel pour Makefile avec des tests ):

#include <stdio.h>

#define BUFFERLEN 4096

int main(){
    // This program reads standard input and calculate frequencies of different
    // bytes and present the frequences for each byte value upon exit.
    //
    // Example:
    //
    //     $ echo "Hello world" | ./a.out
    //
    // Copyright (c) 2015 Björn Dahlgren
    // Open source: MIT License

    long long tot = 0; // long long guaranteed to be 64 bits i.e. 16 exabyte
    long long n[256]; // One byte == 8 bits => 256 unique bytes

    const int bufferlen = BUFFERLEN;
    char buffer[BUFFERLEN];
    int i;
    size_t nread;

    for (i=0; i<256; ++i)
        n[i] = 0;

    do {
        nread = fread(buffer, 1, bufferlen, stdin);
        for (i = 0; i < nread; ++i)
            ++n[(unsigned char)buffer[i]];
        tot += nread;
    } while (nread == bufferlen);
    // here you may want to inspect ferror of feof

    for (i=0; i<256; ++i){
        printf("%d ", i);
        printf("%f\n", n[i]/(float)tot);
    }
    return 0;
}

usage:

gcc main.c
cat my.file | ./a.out
Bjoern Dahlgren
la source
Avez-vous un test? Il n'y a aucun commentaire dans le code. En général, ce n'est pas une bonne idée d'utiliser du code non testé et de publier du code non testé ou non commenté - peu importe que ce soit une pratique courante. La possibilité de revoir les révisions est également limitée sur cette plateforme, pensez à une plateforme d'hébergement de code explicite.
Karl Richter
Les tests @KarlRichter étaient une bonne idée à ajouter. J'ai trouvé l'ancienne version obstruée par des caractères '\ 0'. Cette version devrait fonctionner (passe quelques tests de base au moins).
Bjoern Dahlgren
fgetsobtient une ligne, pas un tampon plein. Vous analysez le tampon complet de 4096 octets pour chaque ligne lue depuis stdin. Vous avez besoin freadici, non fgets.
Stéphane Chazelas
@ StéphaneChazelas génial - ne connaissait pas la frayeur (fait rarement les E / S de C). exemple mis à jour pour utiliser fread à la place.
Bjoern Dahlgren
J'ai ajouté un ifbloc autour des instructions printf, ce qui rend la sortie plus lisible si certains octets ne se produisent pas dans le fichier d'entrée: gist.github.com/martinvonwittich/…
Martin von Wittich
3

Comme moyenne, sigma et CV sont souvent importants pour juger les données statistiques du contenu des fichiers binaires, j'ai créé un programme cmdline qui représente toutes ces données comme un cercle ascii de déviations d'octets par rapport à sigma.
http://wp.me/p2FmmK-96
Il peut être utilisé avec grep, xargs et d'autres outils pour extraire des statistiques. entrez la description de l'image ici

circulosmeos
la source
1

Le recodeprogramme peut le faire rapidement même pour des fichiers volumineux, soit des statistiques de fréquence soit pour les octets, soit pour les caractères de différents jeux de caractères. Par exemple, pour compter les fréquences d'octets:

$ echo hello there > /tmp/q
$ recode latin1/..count-characters < /tmp/q
1  000A LF   1  0020 SP   3  0065 e    2  0068 h    2  006C l    1  006F o
1  0072 r    1  0074 t

Attention - spécifiez votre fichier à recoder comme entrée standard, sinon il le remplacera silencieusement par les fréquences des caractères!

Utilisez recode utf-8/..count-characters < filepour traiter le fichier d'entrée comme utf-8. De nombreux autres jeux de caractères sont disponibles et il échouera si le fichier contient des caractères illégaux.

nealmcb
la source
0

Ceci est similaire à la odréponse de Stéphane mais il montre la valeur ASCII de l'octet. Il est également trié par fréquence / nombre d'occurrences.

xxd -c1 my.file|cut -c10-|sort|uniq -c|sort -nr

Je ne pense pas que ce soit efficace car de nombreux processus sont démarrés mais c'est bon pour les fichiers simples, en particulier les petits fichiers.

brendan
la source