оптимизация sed (большая модификация файла на основе меньшего набора данных)

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

Моя новая задача включает некоторое редактирование строки на основе данных из другого файла.

Файл данных (который должен быть изменен) содержит 1 500 000 строк, каждый из них, например, 800 символов в длину. Каждая строка уникальна, и содержит только один личный номер, каждый личный номер уникален),

Файл модификатора, например, 1 800 строк долго, содержит личный номер, и сумму и дату, которая должна быть изменена в файле данных.

Я просто преобразовал (с Vim regex) файл модификатора к sed, но это очень неэффективно.

Скажем, у меня есть строка как это в файле данных:

(some 500 character)id_number(some 300 character)

И я должен изменить данные в 300 символьных частях.

На основе файла модификатора я придумываю sed строки как это:

/id_number/ s/^\(.\{650\}\).\{20\}/\1CHANGED_AMOUNT_AND_DATA/

Таким образом, у меня есть 1 800 строк как это.

Но я знаю, что даже на очень быстром сервере, если я делаю a

sed -i.bak -f modifier.sed data.file

Это очень медленно, потому что это должно считать каждый шаблон x каждая строка.

Нет ли лучший путь?

Примечание: Я не программист, никогда не учился (в школе) об алгоритмах. Я могу использовать awk, sed, устаревшую версию жемчуга на сервере.

8
задан Brian Tompsett - 汤莱恩 5 November 2015 в 10:12
поделиться

6 ответов

Мои предлагаемые подходы (в желаемом порядке) заключаются в обработке этих данных как:

  1. База данных ( даже простая база данных на основе SQLite с индексом будет работать намного лучше, чем sed / awk с файлом размером 10 ГБ)
  2. Плоский файл с фиксированной длиной записи
  3. Плоский файл, содержащий записи переменной длины

Использование базы данных требует заботиться обо всех тех мелких деталях, которые замедляют обработку текстового файла (поиск нужной записи, изменение данных, сохранение их обратно в БД). Взгляните на DBD :: SQLite в случае Perl.

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

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

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

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

6
ответ дан 5 December 2019 в 14:06
поделиться

, я предлагаю вам программу, написанную на Perl (поскольку я не гуру sed / awk, и я не знаю, на что они в точности способны).

Ваш "алгоритм" прост: вам нужно, прежде всего, построить хэш-карту, которая могла бы дать вам новую строку данных для применения для каждого идентификатора. Конечно, это достигается чтением файла модификатора.

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

Я тоже не гуру Perl, но считаю, что программа довольно проста. Если вам нужна помощь в написании, попросите: -)

3
ответ дан 5 December 2019 в 14:06
поделиться

В perl вы должны использовать substr для получения id_number, особенно если id_number имеет постоянную ширину.

my $id_number=substr($str, 500, id_number_length);

После этого, если $ id_number находится в диапазоне, вы должны использовать substr для замены оставшегося текста.

substr($str, -300,300, $new_text);

Регулярные выражения Perl работают очень быстро, но не в этом случае.

2
ответ дан 5 December 2019 в 14:06
поделиться

Я предлагаю не использовать базу данных. Хорошо написанный скрипт Perl превосходит базу данных по порядку величины в задачах такого рода. Поверьте, у меня много практического опыта с этим. Вы не будете импортировать данные в базу данных, когда Perl будет завершен.

Когда вы напишете 1500000 строк с 800 символами, мне кажется, что это 1,2 ГБ. Если у вас очень медленный диск (30 МБ / с), вы прочитаете его за 40 секунд. Лучше 50 -> 24 с, 100 -> 12 и так далее. Но скорость поиска хэша perl (например, db join) на ЦП с частотой 2 ГГц превышает 5 Mlookups / s. Это означает, что ваша работа, связанная с ЦП, будет в секундах, а ваша работа, связанная с вводом-выводом, будет за десятки секунд. Если это действительно 10 ГБ, номера изменятся, но пропорция останется прежней.

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

<id><tab><position_after_id><tab><amount><tab><data>

Мы будем читать данные из стандартного ввода и записывать в стандартный вывод, а сценарий может выглядеть примерно так:

my $modifier_filename = 'modifier_file.txt';

open my $mf, '<', $modifier_filename or die "Can't open '$modifier_filename': $!";
my %modifications;
while (<$mf>) {
   chomp;
   my ($id, $position, $amount, $data) = split /\t/;
   $modifications{$id} = [$position, $amount, $data];
}
close $mf;

# make matching regexp (use quotemeta to prevent regexp meaningful characters)
my $id_regexp = join '|', map quotemeta, keys %modifications;
$id_regexp = qr/($id_regexp)/;     # compile regexp

while (<>) {
  next unless m/$id_regexp/;
  next unless $modifications{$1};
  my ($position, $amount, $data) = @{$modifications{$1}};
  substr $_, $+[1] + $position, $amount, $data;
}
continue { print }

На моем ноутбуке требуется около полминуты для 1,5 миллиона строк, 1800 идентификаторов поиска. , 1,2 ГБ данных. Для 10 ГБ это не должно превышать 5 минут. Разумно ли это быстро для вас?

Если вы начинаете думать, что не привязаны к вводу-выводу (например, если используете какой-то NAS), но привязаны к ЦП, вы можете пожертвовать некоторой удобочитаемостью и изменить это:

my $mod;
while (<>) {
  next unless m/$id_regexp/;
  $mod = $modifications{$1};
  next unless $mod;
  substr $_, $+[1] + $mod->[0], $mod->[1], $mod->[2];
}
continue { print }
1
ответ дан 5 December 2019 в 14:06
поделиться

Вы почти наверняка должны использовать базу данных, как предложил MikeyB .

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

Если вы дойдете до точки, где даже список модификаций становится огромным, вам нужно отсортировать оба файла по их идентификаторам, а затем выполнить слияние списков - в основном:

  1. Сравните идентификатор вверху входного файла с идентификатором на "верх" файла модификаций
  2. Отрегулируйте запись соответствующим образом, если они совпадают.
  3. Запишите ее
  4. Отбросьте "верх"строки из любого файла, имеющего (в алфавитном или числовом порядке) наименьший идентификатор, и прочитать другую строку из этого файла
  5. Перейти к 1.

За кулисами база данных почти наверняка будет использовать объединение списков, если вы выполните это изменение с помощью одного Команда SQL UPDATE .

0
ответ дан 5 December 2019 в 14:06
поделиться

Хорошая сделка по выбору sqlloader или datadump. Это правильный путь.

0
ответ дан 5 December 2019 в 14:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: