how to change \\n to \n in perl - perl

I have a $string including special char following slash such as "1234\\n567\\t";
Note "\\n" and "\\t" are all two char, not three char. so when they are displayed, they are displayed as "\n" and "\t".
For some reason, I need to change the string as : "1234\n567`efg\t". How to do that in a simple way?
Here is my code, which I think can be improved:
#src_array = split(//, $string);
#des_array = ();
$size = $#src_array + 1;
$converting = 0;
for ($i = 0, $j = 0; $i< $size; $i++){
if ($converting == 1) {
# convert and replace '\\n' with '\n'
$des_array[$j++] = $slash_char{$src_array[$i]};
$converting = 0;
}
elsif ($src_array[$i] == '\\'){
$converting = 1;
}
else {
$des_array[$j++] = $src_array[$i];
}
}
my %slash_char = (
'a' => '\a',
'b' => '\b'
...
);

Why not:
s/\\n/\n/g;
s/\\t/\t/g;
It would be nice to use tr///, but that can't transform two characters to one.

Well, the easy way is to use String::Escape:
use String::Escape 'unbackslash';
my $string = "1234\\n567\\t";
my $new_string = unbackslash($string);

Related

Trouble with simple arithmetic in perl

At the moment I'm having trouble doing simple arithmetic seen in the code snippet below, I think the problem may be due to scope but I'm fairly new and don't fully comprehend the scope problem that I may be facing, any help would be greatly appreciated. Thanks.
use POSIX;
use integer;
my $firstnumber1 = 0;
my $secondnumber1 = 0;
my $digitcount = 0;
my $string = "ADD(5,4);";
if($string =~ /^ADD/)
{
foreach my $char (split //, $string)
{
print "char = $char\n";
if((isdigit($char)) && ($digitcount == 0))
{
$firstnumber1 = int($char);
print "firstnumber = $firstnumber1\n";
}
if((isdigit($char)) && ($digitcount == 1))
{
$secondnumber1 = int($char);
print "secondnumber = $secondnumber1\n";
}
$digitcount++;
my $finalnumber1 = $firstnumber1 + $secondnumber1
}
}
print "$finalnumber1 = $firstnumber1 + $secondnumber1";
You're writing a parser in a language you don't fully know yet. Parsers are hard, so I think you should start with something else
You must always use strict and use warnings 'all' at the top of every Perl program you write. That would have alerted you top finalnumber1 not being declared. And all declarations should be made as late as possible -- usually where they are first defined
It's not clear what you intended to happen after the second number! Don't use overly long identifiers like $firstnumber1 etc., and if you find that you're using identifiers with numbers at the end then it's a sign that you need an array instead
Here's my take on what you were trying to do
use strict;
use warnings 'all';
use v5.10;
my ($n1, $n2);
my $nc = 0;
my $total = 0;
my $string = 'ADD(5,4);';
if ( $string =~ /^ADD/ ) {
for my $char ( split //, $string ) {
say "char = $char";
if ( $char =~ /[0-9]/ ) {
if ( $nc == 0 ) {
$n1 = $char;
say "firstnumber = $n1";
}
else {
$n2 = $char;
say "secondnumber = $n2";
}
$total += $char;
++$nc;
}
}
}
say "$total = $n1 + $n2";
output
char = A
char = D
char = D
char = (
char = 5
firstnumber = 5
char = ,
char = 4
secondnumber = 4
char = )
char = ;
9 = 5 + 4
Looks like the $digitCount increment should be inside the two if blocks. Right now you'll increment it when you process the A and then on the D, etc. so by the time you get to the 5 $digitCount will be 4 and you're if conditions will never be true.

How to read multiple text file filled with columns calculating statistics perl

I'm running some network measurements where I now get a ton of timestamps according to this picture:
http://i.stack.imgur.com/TNnBf.png
I want to get the statistics of Mean, Var & CoV and the problem I have is that I don't really know how to read in the text files so I get the right values in the right array.
I saw that Bash was considered poor in this area so I decided that perl should be better. This is my attempt so far:
#!/usr/bin/perl
#foreach $line
$pkt_number = $1;
$pkt_arrival = $2;
$pkt_size = $6;
$nr_of_pkg = 16;
$linespeed = 100000;
$sum_of_throughput = 0;
$throughput = 0;
$sum_of_throughputsquared = 0;
$mean = 0;
$var = 0;
$co_v = 0;
$duration = $pkt_arrival[0] - $pkt_arrival[15];
for (my $i = 0 ; $nr_of_pkg > $pkt_number[i] ; $i++) {
$throughput[i] = $packet_size[i] / $pkt_arrival[i];
$sum_of_throughput = $sum_of_throughput + $throughput[i];
$sum_of_throughput_squared = $sum_of_throughput_squared + $throughput[i] * $throughput[i];
}
$mean = $sum_of_throughput / $nr_of_pkg;
$var = (($sum_of_throughput * $sum_of_throughput) - $sum_of_throughput_squared);
$co_v = sqrt($var)
This is the code to read and parse the text file.
my #pkt_number;
my #pkt_arrival;
my #pkt_size;
my $i = 0;
my $line;
while ( <> ) {
$line = " $_"; # make sure it always start with a whitespace
chomp $line;
( $pkt_number[$i], $pkt_arrival[$i], $pkt_size[$i] )
= (split( /\s+/, $line, 8 ))[ 1, 2, 6 ];
printf "[%s]\n", join("/", $pkt_number[$i], $pkt_arrival[$i], $pkt_size[$i]);
$i++;
}
And this is how you run it on Linux :
% cat result.txt | ./your_perl.pl

Counting the frequencies of bases using for loop and substr in perl

I'm trying to count the number of bases using a for loop and the substr function but the counts are off and I'm not sure why! Please help! I have to use these functions in my assignment. Where am I going wrong? Here is my code:
use strict;
use warnings;
my $user_input = "accgtutf5";
#initalizing the lengths
my $a_base_total = 0;
my $c_base_total = 0;
my $g_base_total = 0;
my $t_base_total = 0;
my $other_total = 0;
for ( my $position = 0; $position < length $user_input; $position++ ) {
my $nucleotide = substr( $user_input, $position, 1 );
if ( $nucleotide eq "a" ) {
$a_base_total++;
} elsif ( $nucleotide eq "c" ) {
$c_base_total++;
} elsif ( $nucleotide eq "g" ) {
$g_base_total++;
} elsif ( $nucleotide eq "t" ) {
$t_base_total++;
} else {
$other_total++;
}
$position++;
}
print "a = $a_base_total\n";
print "c = $c_base_total\n";
print "g = $g_base_total\n";
print "t = $t_base_total\n";
print "other = $other_total\n";
The output I'm getting is :
a=1
c=1
g=0
t=2
other=1
When it should be:
a = 1
c = 2
g = 1
t = 2
other = 3
Thanks in advance! :)
You're incrementing twice.
Simply remove this line:
$position++;
Also, instead of iterating on position, I would suggest iterating on character.
Your script can be simplified to just:
use strict;
use warnings;
my $user_input = "accgtutf5";
my %count;
for my $nucleotide (split '', $user_input) {
$nucleotide = 'other' unless $nucleotide =~ /[acgt]/;
$count{$nucleotide}++;
}
printf "%s = %d\n", $_, $count{$_} // 0 for qw(a c g t other);
You are incrementing $position twice: once at the for and once at the end of the loop. Remove the second $position++.

Perl, Count characters and break at the end of a word

I'm trying to break on 300 characters, which it does fine but I don’t want it to break in the middle of a word. I would like to find either the end of a word or on the next available whitespace. Any help would be gratefully appreciated.
foreach (#$comp) {
my $c = $_;
$c->{reviews} = ($c->{reviews} - 1);
my $stars;
if($c->{reviews} == 0) {
$stars = 0;
} else {
$stars = int($c->{reviews_total} / $c->{reviews});
}
$c->{stars} = $stars;
if($c->{title}) {
$c->{name} = $c->{title};
$c->{event} = "FOO";
}
$c->{description} =~ s/BREAK//g;
my (#desc) = split(//, $c->{description});
my $adesc;
my $rowc = 0;
my $count = 0;
while($count < 300 ) {
$adesc .= #desc[$count];
$rowc++;
$count++;
}
$c->{description} = $adesc;= '...';
}
Let's see what perldoc perlfaq4 has to say:
How do I reformat a paragraph?
Use Text::Wrap (part of the standard Perl distribution):
use Text::Wrap;
print wrap("\t", ' ', #paragraphs);
The paragraphs you give to Text::Wrap should not contain embedded
newlines. Text::Wrap doesn't justify the lines (flush-right).
But what if you don't like it?
Visit CPAN
Text::Wrap::Smart
use Text::Wrap::Smart 'wrap_smart';
use feature 'say';
my $text = 'lorem ipsum dolor sit amet' x 500;
my $options = {
no_split => 1, # Activates 'fuzzy matching'
max_msg_size => 300,
};
say for wrap_smart( $text, $options );
Text::Flow::Wrap
Text::Reform
Unicode::LineBreak
How about:
my $str = q!A very long string to be wrapped on whitespace!;
my $len = 15; #for test. In your case, let $len=300
my #l = grep{s/^\s+//;$_ ne ''}split(/(.{1,$len})(?=\s)/, $str);
dump#l;
output:
("A very long", "string to be", "wrapped on", "whitespace")
You can use:
$str = "my big string blah blah...";
for ( $i = 300; substr( $x, $i, 1 ) =~ /\w/; $i++ ) { };
$str = substr( $str, 0, $i );
perl -nwE 'say for m/(.{0,75} )/g' textsample2.txt
HAMLET: To be, or not to be--that is the question: Whether 'tis nobler in
the mind to suffer The slings and arrows of outrageous fortune Or to take
...
Works like a charm. Unless you have extremely long words in your text, just tweak the numbers.
You'll have to remove all old linebreaks first, of course. So, something like:
sub wrap_text {
my ($text, $len) = #_;
$text =~ s/[\r\n]+//g; # yeah, removing \r while at it
return ( $text =~ m/(.{0,$len} )/g );
}
And then:
say for wrap_text($text, 300);

How can I convert a number to its multiple form in Perl?

Do you know an easy and straight-forward method/sub/module which allows me to convert a number (say 1234567.89) to an easily readable form - something like 1.23M?
Right now I can do this by making several comparisons, but I'm not happy with my method:
if($bytes > 1000000000){
$bytes = ( sprintf( "%0.2f", $bytes/1000000000 )). " Gb/s";
}
elsif ($bytes > 1000000){
$bytes = ( sprintf( "%0.2f", $bytes/1000000 )). " Mb/s";
}
elsif ($bytes > 1000){
$bytes = ( sprintf( "%0.2f", $bytes/1000 )). " Kb/s";
}
else{
$bytes = sprintf( "%0.2f", $bytes ). "b/s";
}
Thank you for your help!
The Number::Bytes::Human module should be able to help you out.
An example of how to use it can be found in its synopsis:
use Number::Bytes::Human qw(format_bytes);
$size = format_bytes(0); # '0'
$size = format_bytes(2*1024); # '2.0K'
$size = format_bytes(1_234_890, bs => 1000); # '1.3M'
$size = format_bytes(1E9, bs => 1000); # '1.0G'
# the OO way
$human = Number::Bytes::Human->new(bs => 1000, si => 1);
$size = $human->format(1E7); # '10MB'
$human->set_options(zero => '-');
$size = $human->format(0); # '-'
Number::Bytes::Human seems to do exactly what you want.
sub magnitudeformat {
my $val = shift;
my $expstr;
my $exp = log($val) / log(10);
if ($exp < 3) { return $val; }
elsif ($exp < 6) { $exp = 3; $expstr = "K"; }
elsif ($exp < 9) { $exp = 6; $expstr = "M"; }
elsif ($exp < 12) { $exp = 9; $expstr = "G"; } # Or "B".
else { $exp = 12; $expstr = "T"; }
return sprintf("%0.1f%s", $val/(10**$exp), $expstr);
}
In pure Perl form, I've done this with a nested ternary operator to cut on verbosity:
sub BytesToReadableString($) {
my $c = shift;
$c >= 1073741824 ? sprintf("%0.2fGB", $c/1073741824)
: $c >= 1048576 ? sprintf("%0.2fMB", $c/1048576)
: $c >= 1024 ? sprintf("%0.2fKB", $c/1024)
: $c . "bytes";
}
print BytesToReadableString(225939) . "/s\n";
Outputs:
220.64KB/s
This snippet is in PHP, and it's loosely based on some example someone else had on their website somewhere (sorry buddy, I can't remember).
The basic concept is instead of using if, use a loop.
function formatNumberThousands($a,$dig)
{
$unim = array("","k","m","g");
$c = 0;
while ($a>=1000 && $c<=3) {
$c++;
$a = $a/1000;
}
$d = $dig-ceil(log10($a));
return number_format($a,($c ? $d : 0))."".$unim[$c];
}
The number_format() call is a PHP library function which returns a string with commas between the thousands groups. I'm not sure if something like it exists in perl.
The $dig parameter sets a limit on the number of digits to show. If $dig is 2, it will give you 1.2k from 1237.
To format bytes, just divide by 1024 instead.
This function is in use in some production code to this day.