This is a test version of Biostars. For the public version, visit https://www.biostars.org.
[Discussion] Parsing Fasta Without Bioperl

We all known the typical fasta format is like:

>id_123 range=chr1_1234_5678 strand=+
atgcatgc

Now I wish to covert them into single one line, like:

id_123 chr1 1234 5678 atgcatgc

The ID and location information can be obtained in the > line, which is easy. But how to print the format i want? I'll use Perl.

  • Regular Expression

Straightforward is to use regular expression.

my $infile =shift;
open IN, $infile;
my $seq = '';
while ( my $line = <IN> ) {
    chomp $line;
    if ($line =~ /^>/){
        print $seq . "\n";
        $seq = '';
        print $line."\t"; #header information, u can do other processing. 
    }
    elsif ($line !~ /\s+/){
        $seq = $seq . $line ; 
    }
}

The above codes will leave the sequence of last gene, since only when the $line=~ /^>/ the code will output the sequence.

Thus, a trick is to add a > as the very bottom line of the file before file processing. BTW, we do have hash available, but for thousands of lines, hash will cost great amount of memory, so I don't like hash.

  • Bio::SeqIO

BioPerl is much more easier.

my $infile =shift;
use Bio::SeqIO;
my $seqio = Bio::SeqIO->new (-file =>$infile, -format=>'fasta');
while (my $seq = $seqio->next_seq){
    my ($id ,$desc, $fullseq) = ($seq->id, $seq->desc, $seq->seq);
# any other processing. Straightforward. 
}

The reason why I don't want to use BioPerl is that not every computer I use will get BioPerl module installed. And this code reminds me of the "Locate non-zero numbers start and end from a string of numbers". Somehow we can treat the > as the first none-zero number ( meanwhile as the start ), and the sequence as zero. But...I cannot figure it out because the > actually induce output, while in the numbers_code.pl the output-inducing duty is on zero number.

Thus, any suggestion?

deleted-post

4 answers

You mean like: cat file.fasta | tr -d '\n' | tr '>' '\n'? :-)

bash command line is fast and almost handle everything. Simple is beautiful.

Just add an extra 'print $seq . "\n";' after your while loop to handle the last sequence.

my $infile =shift;
open IN, $infile;
my $seq = '';
while ( my $line = <IN> ) {
    chomp $line;
    if ($line =~ /^>/){
        print $seq . "\n";
        $seq = '';
        print $line."\t"; #header information, u can do other processing. 
    }
    elsif ($line !~ /\s+/){
        $seq = $seq . $line ; 
    }
}

print $seq . "\n";

Yeah, also works. Thanks, but checking whether it's the end of the file, like @Farhat mentioned, will cover other similar issues.

You can change the condition

if ($line =~ /^>/){...

to

 if (($line =~ /^>/) || eof(IN))

to check for end of file.

Yes!!! i know how to use regular expression, how to change the default newline $/. Other guys's replies all work for me. but I don't know how to check whether it's EOF. Thanks!!!

Because it's Perl, you have more than one way to do anything (even when it's wrong). You can read the Fasta file in blocks:

#!/usr/bin/perl
use strict;
use warnings;

$/ = "\n>";
while (<>) {
    s/>//g;
    my ($com, @seq) = split (/\n/, $_);
    print "$com\t";
    print join "", @seq;
    print "\n";
}

Thank you. And it's really great. Changing $/ from the default "\n" to ">" is wise way to parse Fasta. By the way, normally I'll add local { $/ = ">"; while(<>) { #processsing};} without changing the default newline "\n"in the same whole script.

yes, you must be careful when you change "$/" in a script, in this case because it's a simple short script isn't a problem, but in large/complex scripts it's better to put in a separate block/routine

Default line separators is the one biggest thing I miss in python... I wish the python devs would add something similar in.

Log in to answer this question.