Я ищу быстрый фильтр, который будет принимать текстовый файл на стандартном вводе, хэшировать каждую строку до первого символа табуляции и помещать строку в другой файл в зависимости от значения хеша (по модулю некоторого количества файлов). Например, что-то вроде:

$ cat > foo
a   1
b   2
c   3
d   4
^D
$ hashit -o bar -n2 < foo
$ cat bar.0
b   2
$ cat bar.1
a   1
c   3
d   4

Хеш-функция должна быть согласована между вызовами.

Это похоже на стандартную утилиту split , но я хочу разделить хеш-содержимое строк, а не просто количество строк на компонент.

4 ответа4

1

Так что вам нужна скорость. Такая скорость, скорее всего, требует C (хотя Perl может быть достаточно оптимизирован). К сожалению, буферизацию сложно сделать вручную в C, и медленно в Perl/Python/Java.

Итак, один из возможных путей к C-решению с наименьшим количеством боли, при условии, что вы можете работать в 64-битной системе и обрабатывать не более нескольких баджиллионов ТБ данных:

  1. открыть выходные файлы
  2. mmap весь входной файл
  3. запомнить текущую позицию
  4. сканировать до табуляции, суммируя значения ASCII по модулю количества файлов (возможно, сначала вычитая 31 из каждого символа), чтобы получить код
  5. сканировать до новой строки или EOF
  6. содержимое mmap 'd. это массив. запись из начальной позиции в новую строку в выходной файл. Используйте write(2) , а не fputs или что-то в этом роде, чтобы не мешать буферизации библиотеки C.
  7. вернуться к 3, пока файл не будет закончен

Дружелюбное ядро позаботится о том, чтобы все происходило в памяти по мере того, как вы достигнете конца, поэтому вам не придется буферизовать себя.

Обратите внимание, что сопоставленный с памятью ввод-вывод не обязательно быстрее, чем вызовы read/write для массового ввода-вывода, что, по сути, и является, но это сделает код существенно проще, чем попытка самостоятельно написать логику буферизации. Решение Python, основанное на этом общем дизайне, также может быть достаточно быстрым.

0

Это (подозрительно похоже на домашнее задание;) проблема звучит как работа для awk

awk '{ print > "FilePrefix."$1%YourModValueHere }'

например

awk '{ print > "bar."$1%3 }'

Обновление для устранения недоразумений:

1) define outputfilePrefix and modoloValue
2) load inputfile linewise as positional parameters
3) iterate over all entries in the first column
   a) calculate CRC (cksum), and modolo CRC
   b) output first positional parameter ($1) to file (prefix.modoloOfCRC )
   c) shift positional parameters one to the left (discarding the current line in position 1)

код: просто введите как одну строку в Bash

preFix="bar"; modolo=3;IFS=$'\n';set $(cat foo); for i in $(cut -f1 foo);do target=$(( $(echo $i | cksum | cut -d ' ' -f1;) % $modolo ));echo $1 >> $preFix.$target; shift; echo $target; done

лучше читаемый для понимания

1) preFix="bar"; modolo=3;
2) IFS=$'\n';set $(cat foo); 
3) for i in $(cut -f1 foo);do 
       target=$(( $(echo $i | cksum | cut -d ' ' -f1;) % $modolo ));
       echo $1 >> $preFix.$target; shift; echo $target; 
   done

если вы поместите его в шеллскрипт, вы можете даже передать в файл (с небольшими изменениями ..) через stdin

0

Вы можете написать скрипт Python для этого ... поскольку вы сказали, что он должен быть быстрым, возможно, CRC - разумный выбор хеш-функции.

Попробуйте что-то вроде этого:

import fileinput
import binascii

for line in fileinput.input():
    modulo = binascii.crc32(line.split()[0]) % splits

Переменная splits должна быть установлена на количество файлов, на которые вы хотите разделить ввод. Вы можете использовать переменную modulo для создания имени файла, где должна быть размещена каждая строка.

0

Насколько я могу судить, стандартных утилит для этого нет, а наивная реализация в Python слишком медленная.

Итак, я реализовал это в C в проекте с открытым исходным кодом, где мне это было нужно, QUAC. Надеюсь, это будет полезно для других. (Я еще не подтолкнул, но это должно произойти в течение нескольких дней.)

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .