私は、出力ファイルにどのバイト値がどのくらい頻繁に表示されるかを非常に大きい(使用可能な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が512MBしかない場合でも、すべてのファイルサイズを処理できる簡単なソリューションはありますか?
答え1
一度に1バイトずつ読み、全体を保存する小さなC(またはPerl、Pythonなど)プログラムを書いてください。合理的なオペレーティングシステムでは、完全に脳のない言語は、合理的に効率的な方法でバッファリングやその他の操作を透過的に処理します。
答え2
これがあなたが探している解決策であるかどうかはわかりませんが、ファイルを複数の小さなファイルに分割し(リンクされたスレッドsplit -b 100MB yourfile
で説明されている方法を適用して)、スプレッドシートを使用して別々のファイルからバイトを計算します。ファイルに必要なソフトウェアを追加してください。
答え3
既存のツールが私が望むことをやっているようでなかったので、私は最も快適な言語であるPythonとJavaで独自に実装された2つの「スクリプト」を試しました。
最初の試み:Python
次のPython 3スクリプトはあらゆるサイズのファイルで動作し、各バイトが発生する頻度を計算します。残念ながら、さらには非常に遅い実行されます。 Raspberry 2でPython 3.5を使用すると、1MBを処理するのに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番目の試みでは、バッファを再利用し、より効率的に動作するJITを含む静的型の言語であるJavaを使用しました。 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;
}