Como contar a quantidade de bytes de um arquivo muito grande, agrupando os mesmos bytes?

Como contar a quantidade de bytes de um arquivo muito grande, agrupando os mesmos bytes?

Estou procurando uma maneira de obter estatísticas sobre uma saída muito grande (várias vezes maior que a RAM disponível), quais valores de bytes nos arquivos estão presentes e com que frequência:

A0 01 00 FF 77 01 77 01 A0

Preciso saber quantos bytes A0 existem neste arquivo, quantos 01 e assim por diante. O resultado poderia ser:

A0: 2
01: 3
00: 1
FF: 1
77: 2

Portanto esta questão está muito próxima da questãoComo contar a quantidade de bytes de um arquivo, agrupando os mesmos bytes?mas nenhuma das respostas existentes funciona para arquivos maiores. Pelo que entendi, todas as respostas exigem uma RAM mínima igual ao tamanho do arquivo a ser testado (até várias vezes).

Portanto, as respostas não funcionam em sistemas com pouca RAM, por exemplo, um Raspberry para processar um arquivo de vários GB.

Existe uma solução simples que funcione em qualquer tamanho de arquivo, mesmo que tenhamos, por exemplo, apenas 512 MB de RAM disponíveis?

Responder1

Prepare um pequeno programa C (ou Perl, Python, qualquer que seja) que leia um byte por vez e mantenha os totais. Qualquer linguagem que não seja totalmente burra em um sistema operacional razoável lidará com o buffer e outras tarefas de forma transparente e razoavelmente eficiente.

Responder2

Não tenho certeza se essa é a solução que você está procurando, mas eu apenas dividiria o arquivo em vários arquivos menores (por exemplo, via split -b 100MB yourfile) aplicando os métodos descritos no tópico que você vinculou e, em seguida, somando os bytes contados nos arquivos separados usando a planilha software de sua escolha.

Responder3

Como não parece haver uma ferramenta que possa fazer o que eu quero, tentei dois "scripts" auto-implementados usando as linguagens em que sou melhor: Python e Java:

1ª tentativa: Python

O script python 3 a seguir funciona em qualquer tamanho de arquivo e conta com que frequência cada byte ocorre. Infelizmente, até funciona muito devagar. Usando Pyhon 3.5 em um Raspberry 2, leva mais de um segundo para processar um Megabyte!

#!/usr/bin/python3
import sys
file_name = sys.argv[1]
count = 0
block_size = 1048576
byte_count = [0] * 256
with open(file_name, "rb") as f:
    data = f.read(block_size)
    while data:
        for b in data:
            byte_count[b] += 1
        count = count + len(data)
        print("%d MiB"%(count / 1048576))
        data = f.read(block_size)

print("read bytes: {}".format(count))
for i in range(0,255):
    b_c = byte_count[i]
    print("{} : {} ({:f} %)".format('0x%02x'%i, b_c,  b_c / count * 100))

2ª tentativa: Java

Na minha segunda tentativa usei Java e parece que uma linguagem de tipo estático com JIT que reutiliza buffers funciona de maneira muito mais eficiente. A versão Java rodando no Java 9 foi 40x mais rápida que a versão Python, embora ambas as versões funcionem da mesma maneira.

  • Compilar:javac CountByteValues.java
  • Correr:java -cp . CountByteValues <filename>

.

// CountByteValues.java
import java.io.FileInputStream;
import java.io.IOException;

public class CountByteValues {

    public static void main(String[] args) {
        try (FileInputStream in = new FileInputStream(args[0])) {
            long[] byteCount = new long[256];
            byte[] buffer = new byte[1048576];
            int read;
            long count = 0;
            while ((read = in.read(buffer)) >= 0) {
                for (int i = 0; i < read; i++) {
                    byteCount[0xFF & buffer[i]]++;
                }
                count += read;
                System.out.println((count / 1048576) + " MB");
            }

            System.out.println("Bytes read: " + count);
            for (int i = 0; i < byteCount.length; i++) {
                System.out.println(String.format("0x%x %d (%.2f%%)", i, byteCount[i], byteCount[i] * 100f / count));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Responder4

Como de costume, um programa C seria o mais rápido.
Em um computador onde o exemplo Perl que você apresentou leva 5 segundos.
O próximo código C leva apenas 0,069s:

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

Copiado dehttps://unix.stackexchange.com/a/209786/232326

informação relacionada