Как собрать статистику появления байтов в двоичном файле?

12

Я хотел бы знать эквивалент

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

представленные в /programming/4174113/how-to-gather-characters-usage-statistics-in-text-file-using-unix-команды для создания статистики использования символов в текстовых файлах для подсчета двоичных файлов простые байты вместо символов, т.е. вывод должен быть в виде

18383 57
12543 44
11555 127
 8393 0

Неважно, если команда занимает столько времени, сколько указано для символов.

Если я применяю команду для символов к двоичным файлам, вывод содержит статистику для произвольных длинных последовательностей непечатаемых символов (я не ищу объяснения этому).

Карл Рихтер
источник

Ответы:

8

С GNU od:

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

Или более эффективно с perl(также выводит count (0) для байтов, которые не встречаются):

perl -ne 'BEGIN{$/ = \4096};
          $c[$_]++ for unpack("C*");
          END{for ($i=0;$i<256;$i++) {
              printf "%3d: %d\n", $i, $c[$i]}}' my.file
Стефан Шазелас
источник
Для того, чтобы числа в первой строке были правильно распознаны, мне нужно было добавить | sort -nи | sort -n -rпо убыванию соответственно (сортировка не была частью вопроса). Сортировка могла бы быть лучше ...
Карл Рихтер
Кажется, немного излишне сортировать весь файл, но у меня все получилось.
Майкл Андерсон
Хороший вопрос @Karl, хотя и не запрашивается, использование sort -nздесь имеет гораздо больше смысла. Ответ обновлен.
Стефан Шазелас
4

Для больших файлов использование сортировки будет медленным. Я написал короткую программу на C для решения эквивалентной проблемы ( см. Эту суть Makefile с тестами ):

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

использование:

gcc main.c
cat my.file | ./a.out
Бьерн Дальгрен
источник
У тебя есть тест? Там нет комментариев в коде. Как правило, не рекомендуется использовать непроверенный и публиковать непроверенный или незакомментированный код, независимо от того, является ли это обычной практикой. Возможность просмотра ревизий также ограничена на этой платформе, рассмотрим платформу для размещения явного кода.
Карл Рихтер
Хорошей идеей было добавить тесты @KarlRichter. Я нашел старую версию, заглушенную символами '\ 0'. Эта версия должна работать (как минимум, проходит несколько базовых тестов).
Бьерн Дальгрен
fgetsполучает строку, а не полный буфер. Вы сканируете 4096-байтовый полный буфер для каждой строки, считываемой из стандартного ввода. Вам нужно freadздесь, а не fgets.
Стефан Шазелас
@ StéphaneChazelas здорово - не знал о хладнокровии (редко делают ввод / вывод из C). обновлен пример использования вместо fread.
Бьерн Дальгрен
Я добавил ifблок вокруг операторов printf, который делает вывод более читабельным, если во входном файле нет байтов
Martin von Wittich
3

Таким образом, сигма и CV часто важны при оценке статистических данных содержимого бинарных файлов. Я создал программу cmdline, которая отображает все эти данные в виде круга отклонений байтов от сигмы ascii.
http://wp.me/p2FmmK-96
Может использоваться с grep, xargs и другими инструментами для извлечения статистики. введите описание изображения здесь

circulosmeos
источник
1

recodeПрограмма может сделать это быстро , даже для больших файлов, либо статистики частот либо для байт или для символов различных наборов символов. Например, чтобы посчитать частоты байтов:

$ 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

Внимание - укажите ваш файл для перекодирования в качестве стандартного ввода, иначе он будет молча заменять его на частоты символов!

Используйте recode utf-8/..count-characters < fileдля обработки входного файла как utf-8. Доступно много других наборов символов, и он не будет работать, если файл содержит недопустимые символы.

nealmcb
источник
0

Это похоже на odответ Стефана, но показывает значение ASCII байта. Он также сортируется по частоте / количеству случаев.

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

Я не думаю, что это эффективно, так как многие процессы запускаются, но это хорошо для отдельных файлов, особенно небольших файлов.

Brendan
источник