Como coletar estatísticas de ocorrência de bytes no arquivo binário?


12

Eu gostaria de saber o equivalente a

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

apresentado em /programming/4174113/how-to-gather-characters-usage-statistics-in-text-file-using-unix-comands para produção de estatísticas de uso de caracteres em arquivos de texto para contagem de arquivos binários bytes simples em vez de caracteres, ou seja, a saída deve estar na forma de

18383 57
12543 44
11555 127
 8393 0

Não importa se o comando leva tanto tempo quanto o comando referenciado para caracteres.

Se eu aplicar o comando para caracteres em arquivos binários, a saída conterá estatísticas para seqüências longas arbitrárias de caracteres não imprimíveis (não busco explicações para isso).

Respostas:


8

Com o GNU od:

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

Ou de forma mais eficiente com perl(também gera uma contagem (0) para bytes que não ocorrem):

perl -ne 'BEGIN{$/ = \4096};
          $c[$_]++ for unpack("C*");
          END{for ($i=0;$i<256;$i++) {
              printf "%3d: %d\n", $i, $c[$i]}}' my.file

Para que os números da primeira linha sejam reconhecidos corretamente, tive que adicionar | sort -ne | sort -n -rpor ordem decrescente, respectivamente (a classificação não fazia parte da pergunta). A classificação pode ser melhor ...
Karl Richter

Parece um pouco exagerado ter que classificar o arquivo inteiro, mas funcionou bem para mim.
Michael Anderson

Bom ponto @Karl, embora não seja solicitado, usar sort -naqui faz muito mais sentido. Resposta atualizada.
Stéphane Chazelas

4

Para arquivos grandes, o uso da classificação será lento. Eu escrevi um pequeno programa em C para resolver o problema equivalente ( veja esta lista do Makefile com testes ):

#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

Você tem um teste? Não há comentários no código. Em geral, não é uma boa idéia usar código não testado e publicar não testado ou não comentado - não importa se é uma prática comum. A possibilidade de revisar revisões também é limitada nesta plataforma, considere uma plataforma explícita de hospedagem de código.
Karl Richter

Os testes do @KarlRichter foram uma boa ideia a acrescentar. Eu encontrei a versão antiga sufocada em caracteres '\ 0'. Esta versão deve funcionar (passa em alguns testes básicos pelo menos).
Bjoern Dahlgren

fgetsobtém uma linha, não um buffer cheio. Você está varrendo o buffer completo de 4096 bytes para cada linha lida no stdin. Você precisa freadaqui, não fgets.
Stéphane Chazelas

@ StéphaneChazelas great - não sabia de medo (raramente as E / S de C). exemplo atualizado para usar o fread.
Bjoern Dahlgren

Eu adicionei um ifbloco em torno das declarações printf, que faz com que a saída mais legível se alguns bytes não ocorrem no arquivo de entrada: gist.github.com/martinvonwittich/...
Martin von Wittich

3

Como média, sigma e CV são frequentemente importantes ao julgar dados estatísticos do conteúdo de arquivos binários, criei um programa cmdline que representa graficamente todos esses dados como um círculo ascii de desvios de bytes do sigma.
http://wp.me/p2FmmK-96
Ele pode ser usado com grep, xargs e outras ferramentas para extrair estatísticas. insira a descrição da imagem aqui


1

O recodeprograma pode fazer isso rapidamente, mesmo para arquivos grandes, estatísticas de frequência, tanto para bytes quanto para caracteres de vários conjuntos de caracteres. Por exemplo, para contar frequências 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

Cuidado - especifique seu arquivo para recodificar como entrada padrão; caso contrário, ele será substituído silenciosamente pelas frequências de caracteres!

Use recode utf-8/..count-characters < filepara tratar o arquivo de entrada como utf-8. Muitos outros conjuntos de caracteres estão disponíveis e falhará se o arquivo contiver caracteres ilegais.


0

Isso é semelhante à odresposta de Stephane, mas mostra o valor ASCII do byte. Também é classificado por frequência / número de ocorrências.

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

Eu não acho que isso seja eficiente, pois muitos processos são iniciados, mas é bom para arquivos únicos, principalmente arquivos pequenos.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.