Мой быстрый ответ был бы, awkно если вы обрабатываете много строк - а я говорю о миллионах - вы, вероятно, увидите реальную выгоду от перехода на «настоящий» язык программирования.
Имея это в виду (и awkуже принятый за ответ), я написал несколько реализаций на разных языках и сравнил их с одним и тем же набором данных из 10 000 строк на SSD PCI-E.
me* (C) 0m1.734s
me (C++) 0m1.991s
me (Python/Pypy) 0m2.390s
me (perl) 0m3.024s
Thor+Glenn (sed|sh) 0m3.353s
me (python) 0m3.359s
jasonwryan+Thor (awk) 0m3.779s
rush (while read) 0m6.011s
Thor (sed) 1m30.947s
me (parallel) 4m9.429s
С первого взгляда C выглядит лучше, но это была свинья, которая бегала так быстро. Pypy и C ++ намного проще писать и работать достаточно хорошо, если вы не говорите о многомиллиардных строках. Если бы это было так, обновление до выполнения всего этого в ОЗУ или на SSD могло бы быть более выгодным вложением, чем улучшение кода.
Очевидно, что за то время, которое я потратил на их изучение, вы могли бы обработать несколько сотен миллионов записей в самом медленном варианте . Если вы можете писать только awkпетли или циклы Bash, делайте это и продолжайте жить. У меня явно было слишком много свободного времени сегодня.
Я также протестировал некоторые многопоточные опции (в C ++ и Python, а также гибриды с GNU parallel), но накладные расходы на потоки полностью перевешивают любые преимущества для такой простой операции (разбиение строк, запись).
Perl
awk( gawkздесь), честно говоря, это будет мой первый порт для тестирования подобных данных, но вы можете делать довольно похожие вещи в Perl. Аналогичный синтаксис, но с немного лучшей ручкой написания.
perl -ane 'open(my $fh, ">", $F[0].".seq"); print $fh $F[1]; close $fh;' infile
питон
Мне нравится Python. Это мой рабочий день, и это просто хороший, надежный и невероятно читаемый язык. Даже новичок может догадаться, что здесь происходит.
with open("infile", "r") as f:
for line in f:
id, chunk = line.split()
with open(id + ".seq", "w") as fw:
fw.write(chunk)
Вы должны помнить, что pythonдвоичный файл вашего дистрибутива - не единственная реализация Python. Когда я запустил этот же тест через Pypy, он был быстрее, чем C без какой-либо дальнейшей логической оптимизации. Имейте это в виду, прежде чем писать Python как «медленный язык».
С
Я начал этот пример, чтобы увидеть, что мы действительно можем заставить делать мой процессор, но, честно говоря, C - это кошмар для кода, если вы не трогали его долгое время. Это имеет дополнительный недостаток, заключающийся в том, что он ограничен 100-символьными строками, хотя его очень просто расширить, мне это просто не нужно.
Моя оригинальная версия была медленнее, чем C ++ и pypy, но после блога об этом я получил некоторую помощь от Джулиана Клода . Эта версия теперь самая быстрая из-за улучшенных буферов ввода-вывода. Это также намного дольше и сложнее, чем все остальное.
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#define BUFLEN (8 * 1024)
int main(void) {
FILE *fp;
FILE *fpout;
char line[100];
char *id;
char *token;
char *buf = malloc(BUFLEN);
fp = fopen("infile", "r");
setvbuf ( fp , buf , _IOLBF, BUFLEN );
while (fgets(line, 100, fp) != NULL) {
id = strtok(line, "\t");
token = strtok(NULL, "\t");
char *fnout = malloc(strlen(id)+5);
fnout = strcat(fnout, id);
fnout = strcat(fnout, ".seq");
fpout = fopen(fnout, "w");
setvbuf ( fpout , NULL , _IONBF , 0 );
fprintf(fpout, "%s", token);
fclose(fpout);
}
fclose(fp);
return 0;
}
C ++
Работает хорошо и гораздо легче писать, чем настоящий C. У вас есть все, что вам нужно (особенно когда речь идет о строках и вводе). Все это означает, что вы можете упростить логику. strtokв C это боров, потому что он обрабатывает всю строку, а затем нам нужно сделать все это утомительное распределение памяти. Он просто перемещается вдоль линии, пока не дойдет до вкладки, и мы вытянем сегменты так, как нам нужно.
#include <fstream>
#include <string>
using namespace std;
int main(void) {
ifstream in("infile");
ofstream out;
string line;
while(getline(in, line)) {
string::size_type tab = line.find('\t', 0);
string filename = line.substr(0, tab) + ".seq";
out.open(filename.c_str());
out << line.substr(tab + 1);
out.close();
}
in.close();
}
GNU Parallel
(Не в версии moreutils). Это хороший лаконичный синтаксис, но OMGSLOW. Я мог бы использовать это неправильно.
parallel --colsep '\t' echo {2} \> {1}.seq <infile
Генератор испытательных жгутов
Вот мой генератор данных для 100000 строк [ATGC] * 64. Это не быстро и улучшения приветствуются.
cat /dev/urandom | tr -dc 'ATGC' | fold -w 64 | awk 'NR>100000{exit}{printf NR"\t"$0"\n"}' > infile
awkвсе еще хороший ответ для чего-то меньшего, чем десятки миллионов. Даже если вы [линейно] масштабируете это до миллиарда строк, C сэкономит вам всего 1,5 часа на Perl и 3,6 часа на awk.