¿Cómo contar la cantidad de bytes en un archivo muy grande, agrupando los mismos bytes?

¿Cómo contar la cantidad de bytes en un archivo muy grande, agrupando los mismos bytes?

Estoy buscando una manera de obtener estadísticas sobre una salida muy grande (varias veces mayor que la RAM disponible), qué valores de bytes están presentes en los archivos y con qué frecuencia:

A0 01 00 FF 77 01 77 01 A0

Necesito saber cuántos bytes A0 hay en este archivo, cuántos 01, etc. El resultado podría ser:

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

Por lo tanto, esta pregunta está muy cerca de la pregunta.¿Cómo contar el número de bytes de un archivo, agrupando los mismos bytes?pero ninguna de las respuestas existentes funciona para archivos más grandes. Según tengo entendido, todas las respuestas requieren una RAM mínima igual al tamaño del archivo que se va a probar (hasta varias veces).

Por lo tanto, las respuestas no funcionan en sistemas con poca RAM, por ejemplo, una Raspberry para procesar un archivo de varios GB.

¿Existe una solución simple que funcione con cualquier tamaño de archivo incluso si, por ejemplo, solo tenemos 512 MB de RAM disponibles?

Respuesta1

Prepare un pequeño programa en C (o Perl, Python, lo que sea) que lea un byte a la vez y mantenga los totales. Cualquier lenguaje que no esté completamente muerto en un sistema operativo razonable manejará el almacenamiento en búfer y otras tareas de forma transparente y razonablemente eficiente.

Respuesta2

No estoy seguro de si esa es la solución que está buscando, pero simplemente dividiría el archivo en varios archivos más pequeños (por ejemplo, mediante split -b 100MB yourfile) aplicando los métodos descritos en el hilo que vinculó y luego sumaría los bytes contados en los archivos separados usando la hoja de cálculo. software de su elección.

Respuesta3

Como no parece existir una herramienta que pueda hacer lo que quiero, probé dos "scripts" autoimplementados usando los lenguajes en los que soy mejor: Python y Java:

1er intento: Python

El siguiente script de Python 3 funciona en cualquier tamaño de archivo y cuenta la frecuencia con la que ocurre cada byte. Desafortunadamente, incluso funciona muy, muy lento. ¡Usando Pyhon 3.5 en una Raspberry 2 requiere más de un segundo para procesar un 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))

Segundo intento: Java

Para mi segundo intento utilicé Java y parece que un lenguaje de tipo estático con JIT que reutiliza buffers funciona mucho más eficiente. La versión de Java que se ejecuta en Java 9 era 40 veces más rápida que la versión de Python, aunque ambas versiones funcionan de la misma manera.

  • 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();
        }
    }
}

Respuesta4

Como es habitual, un programa en C sería el más rápido.
En una computadora donde el ejemplo de Perl que presentó toma 5 segundos.
El siguiente código C tarda sólo 0,069 s:

#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

información relacionada