How to concatenate reverse if statement to string? - string

use DateTime::Format::Natural;
$parser = new DateTime::Format::Natural;
$dt = $parser->parse_datetime("1 Test 2010");
print "Date: " . ($dt->dmy('/') if $parser->success);
Why doesn't the last line compile?

It doesn't compile because that form of if is a statement modifier; it can only be used at the end of a statement, not elsewhere in an expression.
You can do:
print "Date: " . ( $parser->success ? $dt->dmy('/') : '' );
or:
print "Date: " . do { $dt->dmy('/') if $parser->success };
(though the latter will try to print $parser->success if it is false, and it will print "Date: 0" in that case).

Perhaps use the ternary operator here?
print 'Date: ' . ($parser->success ? $dt->dmy('/') : '');
Or split the code into two statements;
print 'Date: ';
print $dt->dmy('/') if $parser->success;

Related

perl number of lines in a string

Using perl, is there any single command which give me the number of lines inside a string?
my $linenum= .... $str ....
It should work for when the string is empty, single line, and multiple lines.
You can count number of newline chars \n in the string (or \r for Mac newline)
my $linenum = $str =~ tr/\n//;
I've adapted #rplantiko's answer into a full subroutine that works the way I picture it, with handling for undef and "". It also knows about how the last line of text can be missing a "\n" and returns the apparent line count ( which is the count of "\n" +1 )
# should work on windows + unix but not the old mac
sub count_lines_in_string {
$_ = shift;
return 0 if( !defined $_ or $_ eq "");
my $lastchar = substr $_, -1,1;
my $numlines = () = /\n/g;
# was last line a whole line with a "\n"?;
return $numlines + ($lastchar ne "\n");
}
say count_lines_in_string("asdf\nasdf\n") ;
say count_lines_in_string undef;
say count_lines_in_string "a";
Try to use a regular expression

How can I get Perl string to keep its original formatting after editing it?

I am attempting to write a code that will encrypt letters with a basic cyclic shift cipher while leaving any character that is not a letter alone. I am trying to do this through the use of a sub that finds the new value for each of the letters. When I run the code now,it formats the result so there is a single space between every encrypted letter instead of keeping the original formatting. I also cannot get the result to be only in lowercase letters.
sub encrypter {
my $letter = shift #_;
if ($letter =~ m/^[a-zA-Z]/) {
$letter =~ y/N-ZA-Mn-za-m/A-Za-z/;
return $letter;
}
else {
return lc($letter);
}
}
print "Input string to be encrypted: ";
my $input = <STDIN>;
chomp $input;
print "$input # USER INPUT\n";
my #inputArray = split (//, $input);
my $i = 0;
my #encryptedArray;
for ($i = 0; $i <= $#inputArray; $i++) {
$encryptedArray[$i] = encrypter($inputArray[$i]);
}
print "#encryptedArray # OUTPUT\n";
The problem is how you are printing the array.
Change this line:
print "#encryptedArray # OUTPUT\n";
to:
print join("", #encryptedArray) . " # OUTPUT\n";
Here is an example that illustrates the problem.
#!/usr/bin/perl
my #array = ("a","b","c","d");
print "#array # OUTPUT\n";
print join("", #array) . " # OUTPUT\n";
Output:
$ perl test.pl
a b c d # OUTPUT
abcd # OUTPUT
According to the Perl documentation on print:
The current value of $, (if any) is printed between each LIST item.
The current value of $\ (if any) is printed after the entire LIST has
been printed.
So two others ways to do it would be:
#!/usr/bin/perl
my #array = ("a","b","c","d");
$,="";
print #array, " #OUTPUT\n";
or
#!/usr/bin/perl
my #array = ("a","b","c","d");
$"="";
print #array, " #OUTPUT\n";
Here is a related answer and here is documentation explaining $" and $,.
Those spaces in your output from $" (list separator) because you use print "#encryptedArray" to print that array, which equals print join($", #encryptedArray), therefore you could disable them by
local $" = '';
or you could join that #encryptedArray by yourself before you print it, just as suggested by #Matt.
Note that there is no need for such complexity. tr/// - also known as y/// - wil convert the whole string for you. Like this
use strict;
use warnings;
print "Input string to be encrypted: ";
chomp(my $input = <STDIN>);
print "$input # USER INPUT\n";
(my $encrypted = $input) =~ tr/N-ZA-Mn-za-m/A-Za-z/;
print "$encrypted # OUTPUT\n";

Why does Perl string become undefined?

What is the proper way to concatenate several scalar values into one Perl string?
The following code is deliberately a series of statements for debugging reasons.
my $bill_record;
$bill_record = $acct_no . " |";
$bill_record = $bill_record . defined($w_ptWtrMtrRecRef->{"mtr_addr_no"}) ? $w_ptWtrMtrRecRef->{"mtr_addr_no"} : " " . " |" ;
$bill_record = $bill_record . defined($w_ptWtrMtrRecRef->{"mtr_addr_str"}) ? $w_ptWtrMtrRecRef->{"mtr_addr_str"} : " " . " |" ;
$bill_record = $bill_record . defined($w_ptWtrMtrRecRef->{"mtr_addr_apt"}) ? $w_ptWtrMtrRecRef->{"mtr_addr_apt"} : " " . " |" ;
$bill_record = $bill_record . $issue_date . " |";
The | character is serving as a delimiter. Each line will be '\n terminated.
After the last line $bill_record = $bill_record . $issue_date . " |";
This error appears:
Use of uninitialized value $bill_record in concatenation (.) or string at /home/ics/include/WsBillFunc.pm line 1022.
at /home/ics/include/WsBillFunc.pm line 1022
$issue_date is defined when assigned.
What could be causing $bill_record to become undefined, and what is the proper way to concatenate a bunch of scalar values into one string?
I don't know specifically why $bill_record is undefined. But if I understand what you're trying to do, you're running into a precedence problem: the ?: operator has lower precedence than concatenation ., so that
$bill_record = $bill_record . defined($w_ptWtrMtrRecRef->{"mtr_addr_no"}) ? $w_ptWtrMtrRecRef->{"mtr_addr_no"} : " " . " |" ;
is treated as
$bill_record = ($bill_record . defined($w_ptWtrMtrRecRef->{"mtr_addr_no"})) ? $w_ptWtrMtrRecRef->{"mtr_addr_no"} : (" " . " |") ;
which I suspect is not what you want. Try adding parentheses:
$bill_record = $bill_record . (defined($w_ptWtrMtrRecRef->{"mtr_addr_no"}) ? $w_ptWtrMtrRecRef->{"mtr_addr_no"} : " ") . " |" ;
(Or use .= as another commenter suggested.)
I'd probably do that in one statement with a join:
$bill_record = join ' |',
map( {
defined( $_ ) ? $_ : ' '
} #{ $w_ptWtrMtrRecRef }{ qw( mtr_addr_no mtr_addr_str mtr_addr_apt ) }
),
$issue_date,
'';
In the map I limit with parens because I only want to apply it to the hash slice. After that is the $issue_date and the empty string. That empty string gets the final | you have.
But, for your problem, it looks like you have a precedence problem. One way to see this is to ask Perl to compile then deparse your program to see what it thinks you wanted. The B::Deparse module does this and I use the -p argument to add extra parentheses.
Here's a cut down version of your original program with the added call to the deparser at the top (it's the B::Deparse module but the namespace is O:
#!/usr/bin/perl
use O qw(Deparse -p);
my $b;
$b = $acct_no . " |";
$b = $b . defined($w->{"no"}) ? $w->{"no"} : " " . " |" ;
$b = $b . defined($w->{"str"}) ? $w->{"str"} : " " . " |" ;
$b = $b . defined($w->{"apt"}) ? $w->{"apt"} : " " . " |" ;
$b = $b . $issue_date . " |";
It outputs:
my($b);
($b = ($acct_no . ' |'));
($b = (($b . defined($$w{'no'})) ? $$w{'no'} : ' |'));
($b = (($b . defined($$w{'str'})) ? $$w{'str'} : ' |'));
($b = (($b . defined($$w{'apt'})) ? $$w{'apt'} : ' |'));
($b = (($b . $issue_date) . ' |'));
The key part is the (($b . defined($$w{'no'})). The current value of $b is concatenated with the return value of defined, then the conditional operator (? :) is done. If the test value is true, it returns the first value in the conditional.
When it gets to mgr_apt_no, there are probably many records that don't have that value set. However, the combined value of the previous $b and $$w{'apt'} is defined because $b is not empty. Thus, it chooses the value of $$w{'apt'} to assign to $b. When it does the last line, $b is empty for the concatenation with $issue_date.

search 2 files and output the common and print an extra line unix scripting

I have this code, but it's giving me an error
awk '
FNR == NR {
# reading get_ids_only.txt
values[$1] = ""
next
}
BEGIN {
# reading default.txt
for (elem in values){
if ($0 ~ elem){
if (values[elem] == ""){
values[elem] = "\"" $0 "\""
getline;
values[elem] = "\n"" $0 ""\n"
}
else{
values[elem] = values[elem] ", \"" $0 "\""
getline;
values[elem] = values[elem] "\n"" $0 ""\n"
}
}
}
END {
for (elem in values)
print elem " [" values[elem] "]"
}
' get_ids_only.txt default.txt
The error says
awk: syntax error at source line 23
context is
>>> END <<< {
awk: illegal statement at source line 24
awk: illegal statement at source line 24
missing }
This is where my END{ } function starts...
What I'm trying to do is.. compare the string.... in file 1.. if the string is found in file 2, print the string and print the line after it as well., then skip a space.
input1:
message id "hello"
message id "good bye"
message id "what is cookin"
input2:
message id "hello"
message value "greetings"
message id "good bye"
message value "limiting"
message id "what is there"
message value "looking for me"
message id "what is cooking"
message value "breakfast plate"
output:
should print out all the input1, grabbing the message value from input 2.
can anyone guide me on why this error is occurring?
I'm using the terminal on my mac.
Here's your BEGIN block with recommended indention and comments, can you see the problem?
BEGIN {
# reading default.txt
for (elem in values){
if ($0 ~ elem){
if (values[elem] == ""){
values[elem] = "\"" $0 "\""
getline;
values[elem] = "\n"" $0 ""\n"
}
else{
values[elem] = values[elem] ", \"" $0 "\""
getline;
values[elem] = values[elem] "\n"" $0 ""\n"
} # End inner if
} # End outer if
} # End for loop
Your missing a closing brace. Note that in the final concatenation with $0, $0 is actually quoted.
There are some other issues with this, I'm not sure what you are trying to do, but it seems a very un-awky approach. Usually if you find yourself overusing getline, you should be thinking about spreading the code into separate blocks with appropriate conditions. See this article on the uses an misuses of getline for more.
A more awky way to solve it
If I understand you correctly, this is the way I would solve this task:
extract.awk
FNR==NR { id[$0]; next } # Collect id lines in the `id' array
$0 in id { f=1 } # Use the `f' as a printing flag
f # Print when `f' is 1
NF==0 { f=0 } # Stop printing after an empty line
Run it like this:
awk -f extract.awk input1 input2
Output:
message id "hello"
message value "greetings"
message id "good bye"
message value "limiting"

Fast Way to Find Difference between Two Strings of Equal Length in Perl

Given pairs of string like this.
my $s1 = "ACTGGA";
my $s2 = "AGTG-A";
# Note the string can be longer than this.
I would like to find position and character in in $s1 where it differs with $s2.
In this case the answer would be:
#String Position 0-based
# First col = Base in S1
# Second col = Base in S2
# Third col = Position in S1 where they differ
C G 1
G - 4
I can achieve that easily with substr(). But it is horribly slow.
Typically I need to compare millions of such pairs.
Is there a fast way to achieve that?
Stringwise ^ is your friend:
use strict;
use warnings;
my $s1 = "ACTGGA";
my $s2 = "AGTG-A";
my $mask = $s1 ^ $s2;
while ($mask =~ /[^\0]/g) {
print substr($s1,$-[0],1), ' ', substr($s2,$-[0],1), ' ', $-[0], "\n";
}
EXPLANATION:
The ^ (exclusive or) operator, when used on strings, returns a string composed of the result of an exclusive or on each bit of the numeric value of each character. Breaking down an example into equivalent code:
"AB" ^ "ab"
( "A" ^ "a" ) . ( "B" ^ "b" )
chr( ord("A") ^ ord("a") ) . chr( ord("B") ^ ord("b") )
chr( 65 ^ 97 ) . chr( 66 ^ 98 )
chr(32) . chr(32)
" " . " "
" "
The useful feature of this here is that a nul character ("\0") occurs when and only when the two strings have the same character at a given position. So ^ can be used to efficiently compare every character of the two strings in one quick operation, and the result can be searched for non-nul characters (indicating a difference). The search can be repeated using the /g regex flag in scalar context, and the position of each character difference found using $-[0], which gives the offset of the beginning of the last successful match.
Use binary bit ops on the complete strings.
Things like $s1 & $s2 or $s1 ^ $s2 run incredibly fast, and work with strings of arbitrary length.
I was bored on Thanksgiving break 2012 and answered the question and more. It will work on strings of equal length. It will work if they are not. I added a help, opt handling just for fun. I thought someone might find it useful.
If you are new to PERL add don't know. Don't add any code in your script below DATA to the program.
Have fun.
./diftxt -h
usage: diftxt [-v ] string1 string2
-v = Verbose
diftxt [-V|--version]
diftxt [-h|--help] "This help!"
Examples: diftxt test text
diftxt "This is a test" "this is real"
Place Holders: space = "·" , no charater = "ζ"
cat ./diftxt
----------- cut ✂----------
#!/usr/bin/perl -w
use strict;
use warnings;
use Getopt::Std;
my %options=();
getopts("Vhv", \%options);
my $helptxt='
usage: diftxt [-v ] string1 string2
-v = Verbose
diftxt [-V|--version]
diftxt [-h|--help] "This help!"
Examples: diftxt test text
diftxt "This is a test" "this is real"
Place Holders: space = "·" , no charater = "ζ"';
my $Version = "inital-release 1.0 - Quincey Craig 11/21/2012";
print "$helptxt\n\n" if defined $options{h};
print "$Version\n" if defined $options{V};
if (#ARGV == 0 ) {
if (not defined $options{h}) {usage()};
exit;
}
my $s1 = "$ARGV[0]";
my $s2 = "$ARGV[1]";
my $mask = $s1 ^ $s2;
# setup unicode output to STDOUT
binmode DATA, ":utf8";
my $ustring = <DATA>;
binmode STDOUT, ":utf8";
my $_DIFF = '';
my $_CHAR1 = '';
my $_CHAR2 = '';
sub usage
{
print "\n";
print "usage: diftxt [-v ] string1 string2\n";
print " -v = Verbose \n";
print " diftxt [-V|--version]\n";
print " diftxt [-h|--help]\n\n";
exit;
}
sub main
{
print "\nOrig\tDiff\tPos\n----\t----\t----\n" if defined $options{v};
while ($mask =~ /[^\0]/g) {
### redirect stderr to allow for test of empty variable with error message from substr
open STDERR, '>/dev/null';
if (substr($s2,$-[0],1) eq "") {$_CHAR2 = "\x{03B6}";close STDERR;} else {$_CHAR2 = substr($s2,$-[0],1)};
if (substr($s2,$-[0],1) eq " ") {$_CHAR2 = "\x{00B7}"};
$_CHAR1 = substr($s1,$-[0],1);
if ($_CHAR1 eq "") {$_CHAR1 = "\x{03B6}"} else {$_CHAR1 = substr($s1,$-[0],1)};
if ($_CHAR1 eq " ") {$_CHAR1 = "\x{00B7}"};
### Print verbose Data
print $_CHAR1, "\t", $_CHAR2, "\t", $+[0], "\n" if defined $options{v};
### Build difference list
$_DIFF = "$_DIFF$_CHAR2";
### Build mask
substr($s1,"$-[0]",1) = "\x{00B7}";
} ### end loop
print "\n" if defined $options{v};
print "$_DIFF, ";
print "Mask: \"$s1\"\n";
} ### end main
if ($#ARGV == 1) {main()};
__DATA__
This is the easiest form you can get
my $s1 = "ACTGGA";
my $s2 = "AGTG-A";
my #s1 = split //,$s1;
my #s2 = split //,$s2;
my $i = 0;
foreach (#s1) {
if ($_ ne $s2[$i]) {
print "$_, $s2[$i] $i\n";
}
$i++;
}

Resources