
私は、非常に大きな(使用可能な RAM の何倍も大きい)出力の統計情報を取得し、ファイル内のバイト値とその頻度を取得する方法を探しています。
A0 01 00 FF 77 01 77 01 A0
このファイルには A0 バイトがいくつあるか、01 がいくつあるかなどを知る必要があります。結果は次のようになります。
A0: 2
01: 3
00: 1
FF: 1
77: 2
したがって、この質問は次の質問に非常に近いです同じバイトをグループ化して、ファイル内のバイト数をカウントするにはどうすればよいですか?しかし、既存の回答はどれも大きなファイルには機能しません。私の理解では、すべての回答には、テストするファイルのサイズ(最大複数倍)に等しい最小 RAM が必要です。
したがって、回答は、数 GB のファイルを処理する Raspberry などの RAM が少ないシステムでは機能しません。
たとえば、使用可能な RAM が 512 MB しかない場合でも、あらゆるファイル サイズで機能する簡単なソリューションはありますか?
答え1
一度に 1 バイトずつ読み取り、合計値を保持する小さな C (または Perl、Python など) プログラムを作成します。適切なオペレーティング システムで完全に無能でない言語であれば、バッファリングやその他の作業を透過的に、かなり効率的に処理します。
答え2
それがあなたが探している解決策かどうかはわかりませんが、リンクしたスレッドで説明されている方法を適用して、ファイルを複数の小さなファイルに分割し (たとえば、 を使用split -b 100MB yourfile
)、選択したスプレッドシート ソフトウェアを使用して、個別のファイルでカウントされたバイトを合計します。
答え3
私が望むことを実行できる既存のツールはないようですので、私が最も得意とする言語である Python と Java を使用して、2 つの自己実装「スクリプト」を試しました。
1回目の試み: Python
次の Python 3 スクリプトは、任意のファイル サイズで動作し、各バイトの発生頻度をカウントします。残念ながら、このスクリプトも非常に低速で動作します。Raspberry 2 で Pyhon 3.5 を使用すると、1 メガバイトを処理するのに 1 秒以上かかります。
#!/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回目の試み: Java
2 回目の試行では 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;
}