Индексирование Огромный текстовый файл - PullRequest
7 голосов
/ 11 апреля 2011

У меня есть один огромный текстовый файл (более 100 гигов) с 6 столбцами данных (табуляция в качестве разделителя).В первом столбце у меня есть целочисленное значение (2500 различных значений в наборе).Мне нужно разделить этот файл на несколько небольших файлов в зависимости от значения в первом столбце (обратите внимание, что строки НЕ сортируются).Каждый из этих небольших файлов будет использоваться для подготовки сюжета в Matlab.

У меня только 8 ГБ оперативной памяти.

Проблема в том, как это сделать эффективно?Есть идеи?

Ответы [ 6 ]

5 голосов
/ 11 апреля 2011

Использование bash:

cat 100gigfile | while read line; do
  intval="$( echo "$line" | cut -f 1)"
  chunkfile="$( printf '%010u.txt' "$intval" )"
  echo "$line" >> "$chunkfile"
done

Это разделит ваш 100-гигабайтный файл на (как вы говорите) 2500 отдельных файлов, названных по значению первого поля.Возможно, вам придется настроить аргумент формата для printf на свой вкус.

2 голосов
/ 11 апреля 2011

однострочник с bash + awk:

awk '{print $0 >> $1".dat" }' 100gigfile 

это добавит каждую строку вашего большого файла к файлу, который называется значением первого столбца + расширение ".dat", например строка 12 aa bb cc dd ee ff перейдет в файл 12.dat.

1 голос
/ 11 апреля 2011

Для Linux 64-битной (я не уверен, что он работает для Windows), вы можете mmap файл и копировать блоки в новые файлы. Я думаю, что это был бы самый эффективный способ сделать это.

0 голосов
/ 11 апреля 2011

Очевидное решение - открывать новый файл каждый раз, когда вы сталкиваетесь с новым значением, и держать его открытым до конца.Но ваша ОС может не позволить вам открыть 2500 файлов одновременно.Так что если вам нужно сделать это только один раз, вы можете сделать это следующим образом:

  1. Просмотрите файл, составив список всех значений.Сортировать этот список.(Вам не нужен этот шаг, если вы заранее знаете, какими будут значения.)
  2. Установите StartIndex равным 0.
  3. Откройте, скажем, 100 файлов (независимо от того, с какой ОС вам удобно)).Они соответствуют следующим 100 значениям в списке, от list[StartIndex] до list[StartIndex+99].
  4. Просмотрите файл, отупутируя эти записи с помощью list[StartIndex] <= value <= list[StartIndex+99].
  5. Закройте все файлы.
  6. Добавьте 100 к StartIndex и перейдите к шагу 3, если вы еще не закончили.

Итак, вам нужно 26 проходов по файлу.

0 голосов
/ 11 апреля 2011

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

Вы будете открывать каждый файл в отдельном файловом дескрипторе, чтобы не открывать и не закрывать каждую строку.Открывайте их все в начале или лениво на ходу.Закройте их все, прежде чем закончить.В большинстве дистрибутивов Linux по умолчанию разрешено открывать только 1024 файла, поэтому вам придется увеличить лимит, скажем, с помощью ulimit -n 2600, если у вас есть разрешение (см. Также /etc/security/limits.conf).

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

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

Интересно, а неблокирующий ввод / вывод решает такие проблемы, как эта?

0 голосов
/ 11 апреля 2011

В твоей раковине ...

$ split -d -l <some number of lines> Foo Foo

Это разделит большой файл Foo на Foo1 до FooN, где n определяется количеством строк в оригинале, разделенным на значение, которое вы указываете для -l. Перебирать куски в цикле ...

РЕДАКТИРОВАТЬ ... хороший момент в комментарии ... этот скрипт (ниже) будет читать строку за строкой, классифицировать и назначать файл на основе первого поля ...

#!/usr/bin/env python
import csv

prefix = 'filename'
reader = csv.reader(open('%s.csv' % prefix, 'r'))
suffix = 0
files = {}
# read one row at a time, classify on first field, and send to a file
# row[0] assumes csv reader does *not* split the line... if you make it do so,
# remove the [0] indexing (and strip()s) below
for row in reader:
    tmp = row[0].split('\t')
    fh = files.get(tmp[0].strip(), False)
    if not fh:
        fh = open('%s%05i.csv' % (prefix, suffix), 'a')
        files[tmp[0].strip()] = fh
        suffix += 1
    fh.write(row[0])

for key in files.keys():
    files[key].close()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...