http://www.perlmonks.org?node_id=986033

supriyoch_2008 has asked for the wisdom of the Perl Monks concerning the following question:

Hi Perl Monks,

I am a beginner in perl. I am interested in counting the number and position of A's and T's in the string $str. My perl code gives correct results in the cmd screen. But the output in the text file does not show the results (possibly the pos function has to be assigned to a variable). I have used the pos function as cited in the PERLQUICK Tutorial (page 8; Example: $x="cat dog house"; and in the Perlretut Tutorial (page:32; Example: while ($dna=~ /(\w\w\w)*?TGA/g){print" Got a TGA stop codon ....}. But I am not getting the results of the cmd screen as a text output on desktop. Would you suggest me how to get the results in a text file? My code goes as:

#!/usr/bin/perl -w $str="BATCATDATEFEAT"; $A=0;$T=0; while ($str=~ /A/ig) {$A++;# Line 4 print"\n A=$A ends at ",pos $str,"\n";} while ($str=~ /T/ig) {$T++; print"\n T=$T ends at ",pos $str,"\n";} $output="Results .txt"; # Line 8 unless (open(RESULT,">$output")){ print"Cannot open file\"$output\".\n\n"; exit; # Line 11 } # Line 12 print RESULT"\n A=$A ends at ",pos $str,"\n T=$T ends at ",pos $str,"\n\n"; close(RESULT); # Line 17 exit;

I have got the correct results in cmd screen:

Microsoft Windows [Version 6.1.7600] Copyright (c) 2009 Microsoft Corporation. All rights reserved. C:\Users\xx>cd desktop C:\Users\xx\Desktop>at.pl A=1 ends at 2 A=2 ends at 5 A=3 ends at 8 A=4 ends at 13 T=1 ends at 3 T=2 ends at 6 T=3 ends at 9 T=4 ends at 14 Use of uninitialized value in print at C:\Users\DR-SUPRIYO\Desktop\at. +pl line 13. Use of uninitialized value in print at C:\Users\DR-SUPRIYO\Desktop\at. +pl line 13.

Output Text File in Desktop shows the wrong results:

A=4 ends at T=4 ends at

Replies are listed 'Best First'.
Re: How can I get the results in a text file from counting in a string?
by kennethk (Abbot) on Aug 07, 2012 at 17:39 UTC

    While there are some cosmetic issues I'd like to raise, the heart of your issue with the posted code is that you have not wrapped your output-to-file call to pos in the same while loop that you wrapped your output-to-screen block. Except, of course, then you try to output them both simultaneously, which is problematic because the first time through, you used two different loops. The following does what (I think) you intend:

    #!/usr/bin/perl -w $str="BATCATDATEFEAT"; $A=0;$T=0; while ($str=~ /A/ig) {$A++;# Line 4 print"\n A=$A ends at ",pos $str,"\n";} while ($str=~ /T/ig) {$T++; print"\n T=$T ends at ",pos $str,"\n";} $output="Results .txt"; # Line 8 unless (open(RESULT,">$output")){ print"Cannot open file\"$output\".\n\n"; exit; # Line 11 } # Line 12 $A=0;$T=0; while ($str=~ /A/ig) {$A++;# Line 4 print RESULT "\n A=$A ends at ",pos $str,"\n";} while ($str=~ /T/ig) {$T++; print RESULT "\n T=$T ends at ",pos $str,"\n";} close(RESULT); # Line 17 exit;
    with minimal changes to code. Note that this opens a file with a white space in the name, which is likely not what you intended and makes dealing with files generally a bigger pain.

    You may want to check out open and die for some more readable code, strict for making debugging easier, and you may want to add line breaks and indents to make flow more obvious. So a cleaner version of this code may look like:

    #!/usr/bin/perl -w use strict; my $str="BATCATDATEFEAT"; my $A=0; my $T=0; while ($str =~ /A/ig) { $A++;# Line 4 print "\n A=$A ends at ",pos $str,"\n"; } while ($str =~ /T/ig) { $T++; print"\n T=$T ends at ",pos $str,"\n"; } my $output="Results.txt"; # Line 8 open(my $fh, ">", $output) or die "Cannot open file '$output'.\n\n"; $A=0; $T=0; while ($str=~ /A/ig) { $A++; print $fh "\n A=$A ends at ",pos $str,"\n"; } while ($str=~ /T/ig) { $T++; print $fh "\n T=$T ends at ",pos $str,"\n"; }

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

      Good advice, definitely cleaner; ++.

      But why require a second time thru each letter? Print to screen and immediately to file. For some, at least, this may rate as a minor improvement.

      #!/usr/bin/perl use 5.014; # 986041 my $str="BATCATDATEFEAT"; my $A=0; my $T=0; my $output = 'D:/_Perl_/PMonks/986041.txt'; open(RESULT,">>$output") or die "Can't open $output, $!"; while ($str=~ /A/ig) { $A++; # OP's Line 4 print "\n A=$A ends at ",pos $str,"\n"; print RESULT "\n A=$A ends at ",pos $str,"\n"; } while ($str=~ /T/ig) { $T++; print "\n T=$T ends at ",pos $str,"\n"; print RESULT "\n T=$T ends at ",pos $str,"\n"; } close(RESULT); # originally, Line 17 exit; =head execution: C:\>type d:\_Perl_\PMonks\986041.txt A=1 ends at 2 A=2 ends at 5 A=3 ends at 8 A=4 ends at 13 T=1 ends at 3 T=2 ends at 6 T=3 ends at 9 T=4 ends at 14 =cut

      (Perhaps obviously) console gets identical output.

        While I certainly think the code could use dramatic algorithmic improvement, I was attempting to keep the logic as consistent as possible, given the OP's apparent level of familiarity. My actual inclination would be to write it as a single pass, stashing results in a hash, so you could separate the processing and the output. Also reduces duplication of code. But to each their own, premature optimization, and all that jazz.
        #!/usr/bin/perl use 5.014; # 986041 my $str="BATCATDATEFEAT"; my $output = 'Results.txt'; open(my $fh, '>', $output) or die "Can't open $output, $!"; my %spots; while ($str =~ /([AT])/ig) { push @{$spots{uc $1}}, pos $str; } for my $key (keys %spots) { my $i = 0; for my $pos (@{$spots{$key}}) { $i++; my $text = "\n $key=$i ends at $pos\n"; print $text; print $fh $text; } } =head execution: C:\>type Results.txt A=1 ends at 2 A=2 ends at 5 A=3 ends at 8 A=4 ends at 13 T=1 ends at 3 T=2 ends at 6 T=3 ends at 9 T=4 ends at 14 =cut

        #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: How can I get the results in a text file from counting in a string?
by trizen (Hermit) on Aug 07, 2012 at 17:55 UTC
    #!/usr/bin/perl use strict; use warnings; my $str = "BATCATDATEFEAT"; my %hash; while ($str =~ /([AT])/ig) { $hash{$1}{cnt}++; $hash{$1}{pos} = pos($str); printf "Position of $1 at: %d\n", $hash{$1}{pos}; } my $output = "Results.txt"; open(my $results_fh, '>', $output) or do { die "[!] Cannot open file '$output'.\n"; }; foreach my $key (sort keys %hash) { print $results_fh <<"EOL"; $key=$hash{$key}{cnt} ends at $hash{$key}{pos} EOL } close $results_fh;
Re: How can I get the results in a text file from counting in a string?
by Kenosis (Priest) on Aug 07, 2012 at 19:16 UTC

    kennethk has provided great targeted script advice. Here's another coding option:

    use Modern::Perl; my ( $str, %positions ) = 'BATCATDATEFEAT'; $str =~ s/(A|T)/$positions{pos($str)+1}=$1/ige; open my $fh, '>', 'Results.txt' or die $!; say $fh "Position of $positions{$_} ends at $_" for sort { $a <=> $b } keys %positions; close $fh;

    Output to file:

    Position of A ends at 2 Position of T ends at 3 Position of A ends at 5 Position of T ends at 6 Position of A ends at 8 Position of T ends at 9 Position of A ends at 13 Position of T ends at 14

    This option uses the e modifier in the substitution to execute the embedded code, which holds position/letter (key/value) pairs in a hash. The printing part numerically sorts the keys, sending the output to Results.txt.

    Hope this helps!

    Update: by modifying the script a little, it can generate your output:

    use Modern::Perl; use Sort::Naturally; my ( $str, %i, %positions ) = 'BATCATDATEFEAT'; $str =~ s/(A|T)/$positions{$1 . '=' . ++$i{$1}}=pos($str)+1/ige; open my $fh, '>', 'Results.txt' or die $!; say $fh "$_ ends at $positions{$_}" for nsort keys %positions; close $fh;

    Output to file:

    A=1 ends at 2 A=2 ends at 5 A=3 ends at 8 A=4 ends at 13 T=1 ends at 3 T=2 ends at 6 T=3 ends at 9 T=4 ends at 14

    Here, Sort::Naturally is used to sort the keys which have mixed content, e.g., A=1.

Re: How can I get the results in a text file from counting in a string?
by johngg (Canon) on Aug 07, 2012 at 22:30 UTC

    You can use a HoH structure to hold the results and then print them out to your file afterwards, sorting the keys in nested loops.

    [johngg@justy Documents]$ perl -Mstrict -Mwarnings -MData::Dumper -E ' > my $str = q{BATCATDATEFEAT}; > my %res; > $res{ $_ } = do { > my $ct = 0; > my $rhPosns = {}; > $rhPosns->{ ++ $ct } = pos $str > while $str =~ m{$_}g; > $rhPosns; > } for qw{ A T }; > print Data::Dumper > ->new( [ \ %res ], [ qw{ *res } ] ) > ->Sortkeys( 1 ) > ->Dumpxs(); > foreach my $let ( sort keys %res ) > { > foreach my $seq ( sort { $a <=> $b } keys %{ $res{ $let } } ) > { > say qq{$let=$seq ends at $res{ $let }->{ $seq }}; > } > }' %res = ( 'A' => { '1' => 2, '2' => 5, '3' => 8, '4' => 13 }, 'T' => { '1' => 3, '2' => 6, '3' => 9, '4' => 14 } ); A=1 ends at 2 A=2 ends at 5 A=3 ends at 8 A=4 ends at 13 T=1 ends at 3 T=2 ends at 6 T=3 ends at 9 T=4 ends at 14 [johngg@justy ~]$

    I hope this is of interest.

    Cheers,

    JohnGG