¿Cómo recopilar estadísticas de aparición de bytes en un archivo binario?

12

Me gustaría saber el equivalente de

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

presentado en /programming/4174113/how-to-gather-characters-usage-statistics-in-text-file-using-unix-comandos para la producción de estadísticas de uso de caracteres en archivos de texto para el conteo de archivos binarios bytes simples en lugar de caracteres, es decir, la salida debe tener la forma de

18383 57
12543 44
11555 127
 8393 0

No importa si el comando tarda tanto como el referenciado para los caracteres.

Si aplico el comando para caracteres a archivos binarios, el resultado contiene estadísticas para secuencias largas arbitrarias de caracteres no imprimibles (no busco explicación para eso).

Karl Richter
fuente

Respuestas:

8

Con GNU od:

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

O más eficientemente con perl(también genera un recuento (0) para bytes que no ocurren):

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
fuente
Para que los números en la primera fila se reconocieran correctamente, tuve que agregar | sort -ny | sort -n -ren orden descendente respectivamente (la clasificación no era parte de la pregunta). La clasificación podría hacerse mejor ...
Karl Richter
Parece un poco exagerado tener que ordenar todo el archivo, pero funcionó bien para mí.
Michael Anderson
Buen punto @Karl, aunque no solicitado, usar sort -naquí tiene mucho más sentido. Respuesta actualizada
Stéphane Chazelas
4

Para archivos grandes, la ordenación será lenta. Escribí un breve programa en C para resolver el problema equivalente ( vea este resumen de Makefile con pruebas ):

#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;
}

uso:

gcc main.c
cat my.file | ./a.out
Bjoern Dahlgren
fuente
¿Tienes una prueba? No hay comentarios en el código. En general, no es una buena idea usar código no probado y publicar código no probado o no comentado, sin importar si es una práctica común. La posibilidad de revisar las revisiones también es limitada en esta plataforma, considere una plataforma de alojamiento de código explícito.
Karl Richter
Las pruebas de @KarlRichter fueron una buena idea para agregar. Encontré la versión anterior ahogada en los caracteres '\ 0'. Esta versión debería funcionar (al menos pasa algunas pruebas básicas).
Bjoern Dahlgren
fgetsobtiene una línea, no un búfer lleno. Está escaneando el búfer completo de 4096 bytes para cada línea leída de stdin. Necesitas freadaquí, no fgets.
Stéphane Chazelas
@ StéphaneChazelas genial - no sabía de fread (rara vez hago E / S desde C). ejemplo actualizado para usar fread en su lugar.
Bjoern Dahlgren
He agregado un ifbloque alrededor de las declaraciones printf, que hace que la salida sea más legible si algunos bytes no aparecen en el archivo de entrada: gist.github.com/martinvonwittich/…
Martin von Wittich
3

Como media, sigma y CV a menudo son importantes al juzgar los datos estadísticos del contenido de los archivos binarios, he creado un programa cmdline que grafica todos estos datos como un círculo ascii de desviaciones de bytes de sigma.
http://wp.me/p2FmmK-96
Se puede usar con grep, xargs y otras herramientas para extraer estadísticas. ingrese la descripción de la imagen aquí

circulosmeos
fuente
1

El recodeprograma puede hacer esto rápidamente incluso para archivos grandes, ya sea estadísticas de frecuencia, ya sea para bytes o para los caracteres de varios conjuntos de caracteres. Por ejemplo, para contar frecuencias de bytes:

$ 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

Precaución : especifique su archivo para recodificarlo como entrada estándar; de lo contrario, lo reemplazará en silencio con las frecuencias de caracteres.

Use recode utf-8/..count-characters < filepara tratar el archivo de entrada como utf-8. Hay muchos otros juegos de caracteres disponibles y fallará si el archivo contiene caracteres ilegales.

nealmcb
fuente
0

Esto es similar a la odrespuesta de Stephane, pero muestra el valor ASCII del byte. También se ordena por frecuencia / número de ocurrencias.

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

No creo que esto sea eficiente ya que se inician muchos procesos, pero es bueno para archivos individuales, particularmente archivos pequeños.

Brendan
fuente