Wie sammelt man Statistiken zum Auftreten von Bytes in einer Binärdatei?

12

Ich würde gerne das Äquivalent von wissen

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

Präsentiert in /programming/4174113/how-to-gather-characters-usage-statistics-in-text-file-using-unix-commands zur Erstellung von Zeichenverwendungsstatistiken in Textdateien für die Zählung von Binärdateien einfache Bytes anstelle von Zeichen, dh die Ausgabe sollte in Form von erfolgen

18383 57
12543 44
11555 127
 8393 0

Es spielt keine Rolle, ob der Befehl so lange dauert wie der angegebene für Zeichen.

Wenn ich den Befehl für Zeichen auf Binärdateien anwende, enthält die Ausgabe Statistiken für beliebig lange Sequenzen nicht druckbarer Zeichen (ich suche keine Erklärung dafür).

Karl Richter
quelle

Antworten:

8

Mit GNU od:

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

Oder effizienter mit perl(gibt auch eine Anzahl (0) für Bytes aus, die nicht vorkommen):

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
quelle
Um erkannte die Zahlen in der ersten Reihe zu bekommen richtig musste ich hinzufügen | sort -nund | sort -n -rjeweils um absteigend (Sortierung war nicht Teil der Frage). Das Sortieren könnte besser sein ...
Karl Richter
Scheint ein wenig übertrieben zu sein, um die gesamte Datei sortieren zu müssen, aber für mich hat es funktioniert.
Michael Anderson
Guter Punkt @Karl, obwohl nicht angefordert, sort -nmacht die Verwendung hier viel mehr Sinn. Antwort aktualisiert.
Stéphane Chazelas
4

Bei großen Dateien ist die Sortierung langsam. Ich habe ein kurzes C-Programm geschrieben, um das äquivalente Problem zu lösen ( siehe diese Übersicht für Makefile mit 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;
}

Verwendung:

gcc main.c
cat my.file | ./a.out
Björn Dahlgren
quelle
Hast du einen Test? Der Code enthält keine Kommentare. Es ist im Allgemeinen keine gute Idee, ungeprüften oder unkommentierten Code zu verwenden und zu veröffentlichen - unabhängig davon, ob dies gängige Praxis ist. Die Möglichkeit, Revisionen zu überprüfen, ist auf dieser Plattform ebenfalls eingeschränkt. Erwägen Sie eine explizite Code-Hosting-Plattform.
Karl Richter,
@ KarlRichter Tests waren eine gute Idee, um hinzuzufügen. Ich habe die alte Version gefunden, die an '\ 0' Zeichen erstickt ist. Diese Version sollte funktionieren (besteht mindestens einige grundlegende Tests).
Björn Dahlgren,
fgetserhält eine Zeile, nicht einen Puffer voll. Sie scannen den 4096-Byte-Vollpuffer für jede Zeile, die von stdin gelesen wird. Du brauchst freadhier nicht fgets.
Stéphane Chazelas
@ StéphaneChazelas toll - wusste nichts von fread (selten mache ich / o von C). aktualisiertes Beispiel, um stattdessen fread zu verwenden.
Björn Dahlgren
Ich habe einen ifBlock um die printf-Anweisungen hinzugefügt , der die Ausgabe lesbarer macht, wenn einige Bytes in der Eingabedatei nicht vorkommen: gist.github.com/martinvonwittich/…
Martin von Wittich
3

Da Sigma und CV häufig wichtig sind, um statistische Daten des Inhalts von Binärdateien zu beurteilen, habe ich ein cmdline-Programm erstellt, das alle diese Daten als ASCII-Kreis von Byte-Abweichungen von Sigma grafisch darstellt.
http://wp.me/p2FmmK-96
Es kann mit grep, xargs und anderen Tools zum Extrahieren von Statistiken verwendet werden. Bildbeschreibung hier eingeben

circulosmeos
quelle
1

Das recodeProgramm kann dies auch für große Dateien schnell erledigen, entweder mit Häufigkeitsstatistiken für Bytes oder für die Zeichen verschiedener Zeichensätze. ZB zum Zählen von Bytefrequenzen:

$ 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

Achtung - geben Sie an, dass Ihre Datei als Standardeingabe neu codiert werden soll, sonst wird sie stillschweigend durch die Zeichenhäufigkeiten ersetzt!

Verwenden Sie recode utf-8/..count-characters < filediese Option, um die Eingabedatei als utf-8 zu behandeln. Viele andere Zeichensätze sind verfügbar, und es schlägt fehl, wenn die Datei ungültige Zeichen enthält.

Nealmcb
quelle
0

Dies ähnelt Stephanes odAntwort, zeigt jedoch den ASCII-Wert des Bytes. Es ist auch nach Häufigkeit / Anzahl der Vorkommen sortiert.

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

Ich denke nicht, dass dies effizient ist, da viele Prozesse gestartet werden, aber es ist gut für einzelne Dateien, insbesondere kleine Dateien.

brendan
quelle