
Я ищу способ получить статистику по очень большому (в несколько раз большему, чем доступная оперативная память) объему выходных данных, какие байтовые значения в файлах присутствуют и как часто:
A0 01 00 FF 77 01 77 01 A0
Мне нужно узнать, сколько байтов A0 в этом файле, сколько 01 и т. д. Результат может быть таким:
A0: 2
01: 3
00: 1
FF: 1
77: 2
Поэтому этот вопрос очень близок к вопросуКак подсчитать количество байтов в файле, сгруппировав одинаковые байты?но ни один из существующих ответов не работает для больших файлов. Насколько я понимаю, все ответы требуют минимального объема оперативной памяти, равного размеру файла для тестирования (до нескольких раз).
Поэтому ответы не работают на системах с небольшим объемом оперативной памяти, например, Raspberry для обработки многогигабайтных файлов.
Существует ли простое решение, которое работает с файлами любого размера, даже если у нас, например, всего 512 МБ оперативной памяти?
решение1
Набросайте маленькую программу на C (или Perl, Python, что угодно), которая считывает по одному байту за раз и сохраняет итоги. Любой язык, который не совсем безмозглый на разумной операционной системе, будет обрабатывать буферизацию и другие рутинные задачи прозрачно и достаточно эффективно.
решение2
Не уверен, что это то решение, которое вы ищете, но я бы просто разделил файл на несколько файлов меньшего размера (например, с помощью split -b 100MB yourfile
), применив методы, описанные в теме, ссылку на которую вы дали, а затем сложив подсчитанные байты в отдельных файлах, используя программное обеспечение для работы с электронными таблицами по вашему выбору.
решение3
Поскольку, похоже, не существует инструмента, который мог бы сделать то, что мне нужно, я попробовал два самостоятельных «скрипта» с использованием языков, в которых я лучше всего разбираюсь: Python и Java:
1-я попытка: Питон
Следующий скрипт python 3 работает с файлами любого размера и подсчитывает частоту появления каждого байта. К сожалению, даже он работает очень-очень медленно. При использовании Python 3.5 на Raspberry 2 требуется более одной секунды для обработки одного мегабайта!
#!/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, и, похоже, статически типизированный язык с JIT, который повторно использует буферы, работает гораздо эффективнее. Версия Java, работающая на Java 9, была в 40 раз быстрее, чем версия Python, хотя обе версии работают одинаково.
- Компиляция:
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