如何計算一個非常大的檔案中的位元組數,將相同的位元組分組?

如何計算一個非常大的檔案中的位元組數,將相同的位元組分組?

我正在尋找一種方法來獲取非常大(比可用 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

相關內容