Моя швидка відповідь була б, awk
але якщо ви обробляєте багато рядків - а я кажу про мільйони - ви, швидше за все, побачите реальну користь від переходу на "справжню" мову програмування.
Зважаючи на це (і awk
вже прийнятий як відповідь), я написав декілька реалізацій на різних мовах і порівняв їх на одному 10000-рядковому наборі даних на PCI-E SSD.
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
або баш-петлі, зробіть це і продовжуйте життя. Я, очевидно, мав занадто багато вільного часу сьогодні.
Я також протестував деякі багатопотокові варіанти (у 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, але після ведення блогів про неї я отримав деяку допомогу від Джуліана Клоде . Ця версія зараз найшвидша через налаштовані буфери IO. Це також набагато довше і більше, ніж все інше.
#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 Paralellel
(Не версія 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.