如何计算一个非常大的文件中的字节数,将相同的字节分组?

如何计算一个非常大的文件中的字节数,将相同的字节分组?

我正在寻找一种方法来获取非常大(比可用 RAM 大几倍)的统计信息,输出文件中存在哪些字节值以及出现的频率:

A0 01 00 FF 77 01 77 01 A0

我需要知道这个文件中有多少个A0字节,有多少个01等等。结果可能是:

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

因此这个问题非常接近这个问题如何计算文件中的字节数,将相同的字节分组?但现有的答案都不适用于较大的文件。据我了解,所有答案都需要至少等于要测试的文件大小的 RAM(最多多次)。

因此,答案不适用于 RAM 较小的系统,例如用于处理多 GB 文件的 Raspberry。

是否有一个简单的解决方案可以处理任何文件大小,即使我们只有 512MB RAM 可用?

答案1

编写一个小型 C(或 Perl、Python,等等)程序,一次读取一个字节并保存总数。任何在合理的操作系统上不是完全无脑的语言都将以相当有效的方式透明地处理缓冲和其他杂务。

答案2

不确定这是否是您正在寻找的解决方案,但我只是将文件分成多个较小的文件(例如通过split -b 100MB yourfile)应用您链接的线程中描述的方法,然后使用电子表格将单独文件中的计数字节相加您选择的软件。

答案3

由于似乎没有现有的工具可以完成我想要的事情,因此我尝试了两个使用我最擅长的语言自行实现的“脚本”:Python 和 Java:

第一次尝试:Python

以下 python 3 脚本适用于任何大小的文件,并计算每个字节出现的频率。不幸的是,即使它运行得非常非常慢。在 Raspberry 2 上使用 Pyhon 3.5 需要超过一秒的时间来处理 1 MB!

#!/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))

第二次尝试:Java

在我的第二次尝试中,我使用了 Java,它似乎是一种带有 JIT 的静态类型语言,可以重用缓冲区,工作效率更高。在 Java 9 上运行的 Java 版本比 Python 版本快 40 倍,尽管两个版本的工作方式相同。

  • 编译:javac CountByteValues.java
  • 跑步: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();
        }
    }
}

答案4

通常,C 程序是最快的。
在您提供的 perl 示例需要 5 秒的计算机中。
接下来的 C 代码只需要 0.069 秒:

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

复制自https://unix.stackexchange.com/a/209786/232326

相关内容