Я часто работаю с данными биологической последовательности (FASTA), которые имеют следующий формат, где начальная левая угловая скобка используется в качестве разделителя для обозначения нового заголовка последовательности. Эти файлы часто имеют перенос текста (кроме заголовков):
>header1
ACTGACTGACTGACTG
ACTGACTGACTGACTG
>header2
CTGGGACTAGGGGGAG
CTGGGACTAGGGGGAG
Часто я хочу избежать чтения всего файла в память, потому что он может занимать много МБ (иногда ГБ), поэтому я стараюсь сосредоточиться на циклах while и чтении построчно. Однако мне часто приходится добавлять дополнительный код, чтобы сделать что-то уникальное вверху или внизу файла. Например, сегодня я хотел удалить обтекание текстом какого-то файла, что казалось таким простым:
while (my $line = <$inputfasta_fh>) {
chomp($line);
if ($line =~ /^>/) {
print $outputfasta_fh "$line\n";
}
else {
print $outputfasta_fh $line;
}
}
Но я понял, что мне нужна новая строка перед всеми заголовками, кроме первого (иначе они будут объединены в конец предыдущей последовательности). Итак, это мой грубый обходной путь.
my $switch = 0;
while (my $line = <$inputfasta_fh>) {
chomp($line);
if ($line =~ /^>/) {
if ($switch == 1) {
print $outputfasta_fh "\n";
}
print $outputfasta_fh "$line\n";
$switch = 1;
}
else {
print $outputfasta_fh $line;
}
}
Раньше у меня были другие проблемы, когда мне нужно было что-то сделать с последней строкой. Например, у меня был скрипт, который читал фасту, сохранял каждый заголовок и затем начинал считать длину его последовательности (опять же построчно), и если она попадала в указанный мной диапазон, я сохранял ее в другой файл. Подсчет прервется, если длина превысит максимум, но я не узнаю, была ли она больше минимума, пока не достигну другого заголовка или конца файла. В последнем случае мне пришлось повторить подпрограмму проверки длины ниже цикла while. Я хотел бы избежать повторения последней части.
my $length = 0;
my $header;
my @line_array;
while (my $line = <$inputfasta_fh>) {
chomp($line);
if ($line =~ /^>/) {
# check if previous sequence had a length within range
if (check_length($length, $minlength, $maxlength) == 1) {
print $outputfasta_fh "$header\n";
print $outputfasta_fh join ("\n", @line_array), "\n";
}
undef @line_array;
$header = $line;
$length = 0;
}
else {
if ($length <= $maxlength) { # no point in measuring any more
push (@linearray, $line);
$length += length($line);
}
}
}
#and now for the last sequence
if (check_length($length, $minlength, $maxlength) == 1) {
print $outputfasta_fh "$header\n";
print $outputfasta_fh join ("\n", @line_array), "\n";
}
sub check_length {
my ($length, $minlength, $maxlength) = @_;
if (($length >= $minlength) && ($length <= $maxlength)) {
return 1;
}
else {
return 0;
}
}
Итак, мой основной вопрос: как указать, что я хочу сделать что-то один раз в цикле, не прибегая к счетчикам или повторению кода вне цикла? Спасибо за любую помощь!
if (eof($inputfasta_fh)) {same subroutine}
- person malcolm   schedule 16.09.2013