我有一个这样的文件:
a AGTACTTCCAGGAACGGTGCACTCTCC
b ATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT
c ATATTAAATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCATCCACTCCACAC
d ATCAGTTTAATATCTGATACGTCCTCTATCCGAGGACAATATATTAAATGGA
e TTTGGCTAAGATCAAGTGTAGTATCTGTTCTTATAAGTTTAATATCTGATATGTCCTCTATCTGA
我想制作a.seq
包含序列的文件AGTACTTCCAGGAACGGTGCACTCTCC
。同样b.seq
包含ATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT
.简而言之,Column1应该用作带有扩展名的输出文件名.seq
,然后它应该有相应的column2序列。我可以通过编写 perl 脚本来做到这一点,但命令行上的任何内容都会有所帮助。希望很快能听到。
答案1
我的第一反应是,awk
但是如果您正在处理大量行(我说的是数百万行),您可能会看到切换到“真正的”编程语言的真正好处。
考虑到这一点(并且awk
已经被视为答案),我用不同的语言编写了一些实现,并在 PCI-E SSD 上的相同 10,000 行数据集上对它们进行了基准测试。
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++ 更容易编写和执行足够好除非你谈论的是数十亿行。如果是这样的话,升级到在 RAM 或 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
我喜欢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
我开始这个例子是为了看看我们真的可以让我的 CPU 做什么,但坦率地说,如果你很长一段时间没有接触过 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 并行
(不是 moreutils 版本)。这是一个很好的简洁语法,但是太慢了。我可能用错了。
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
答案2
答案3
纯shell实现:
while read -r filename content ; do
printf '%s\n' "$content" >> "${filename}.seq"
done < /source/file
答案4
这是使用 GNU sed 实现此目的的一种方法:
<infile sed -r 's:(\w+)\s+(\w+):echo \2 > \1.seq:e; d'
或者更有效,如建议的格伦·杰克曼:
<infile sed -r 's:(\w+)\s+(\w+):echo \2 > \1.seq:' | sh