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.
Related
I'm trying to implement a Perl function for creating SAS tokens. Doing all according to documentation (https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas) I always get error "Signature fields not well formed".
My testing resource is "https://ribdkstoragepoc.blob.core.windows.net/project-poc/foo/koteyka.jpg". Variables populated by my code:
signedpermissions = 'r'
signedexpiry = '2020-05-01T07:59:00Z'
canonicalizedresource = '/blob/ribdkstoragepoc/project-poc/foo/koteyka.jpg'
signedresource = 'b'
signedversion = '2018-03-28'
Other variables are just empty lines. I generate a string using the following pattern:
my $stringToSign =
$permissions . "\n" .
$start . "\n" .
$expiry . "\n" .
$canonicalizedResource . "\n" .
$identifier . "\n" .
$IP . "\n" .
$protocol . "\n" .
$version . "\n" .
$resource . "\n" .
$snapshotTime . "\n" .
$rscc . "\n" .
$rscd . "\n" .
$rsce . "\n" .
$rscl . "\n" .
$rsct;
The string to sign:
r
2020-05-01T07:59:00Z
/blob/ribdkstoragepoc/project-poc/foo/koteyka.jpg
2018-03-28
b
Calculationg of signature: my $sig = Digest::SHA::hmac_sha256_base64($stringToSign, $key);
At last the final URL looks like: https://ribdkstoragepoc.blob.core.windows.net/project-poc/foo/koteyka.jpg?sig=YcwWvOT2FtOZGbXQxMAoSxvA2HhRmMAUp%2B6WUY%2Bjbjg&sv=2018-03-28&se=2020-05-01T07%3A59%3A00Z&sr=b&sp=r
As I have already said that does not work. Does anyone have ideas what could be wrong?
Figured out what's wrong with your code. Basically you're using 2018-03-28 version of Storage REST API so you don't need to include $resource and $snapshotTime in your $stringToSign.
Furthermore, you will need to pad your signature with 1-4 = (Ref: https://github.com/smarx/waz-storage-perl/blob/master/WindowsAzure/Storage.pm#L42).
Based on these, here's the code:
use strict;
use warnings;
use Digest::SHA qw(hmac_sha256_base64);
use MIME::Base64;
my $permissions = 'r';
my $start = '';
my $expiry = '2020-01-31T00:00:00Z';
my $canonicalizedResource = '/blob/ribdkstoragepoc/project-poc/foo/koteyka.jpg';
my $identifier = '';
#my $resource = 'b';
my $IP = '';
my $version = '2018-03-28';
my $protocol = '';
#my $snapshotTime = '';
my $rscc = '';
my $rscd = '';
my $rsce = '';
my $rscl = '';
my $rsct = '';
my $stringToSign = $permissions . "\n" . $start . "\n" . $expiry . "\n" . $canonicalizedResource . "\n" . $identifier . "\n" . $IP . "\n" . $protocol . "\n" . $version . "\n" . $rscc . "\n" . $rscd . "\n" . $rsce . "\n" . $rscl . "\n" . $rsct;
print $stringToSign;
my $accountKey = 'your-base64-account-key';
my $sig = Digest::SHA::hmac_sha256_base64($stringToSign, decode_base64($accountKey));
$sig .= '=' x (4 - (length($sig) % 4));
print "\n---------------------\n";
print $sig;
print "\n";
Give this a try, it should work.
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;
I had the code working at one time but i changed something and it quit working.
The $id gets passed to the address bar on the browser but not to the next page.
I used the session_start
while($row = mysql_fetch_array($resultd))
{
$id = $row['id_num'];
echo "Edit "; //ln68
echo "<a href='del.php?id_num = $id'>Delete</a>";
echo $row['id_num'] . " " . $row['first_name'] . " " . $row['last_name'] . ", " . $row['title'] . ", " . $row['city'] . ", " . $row['phone_pri'] . ", " . $row['email_addr'];
echo ""; }
The receiving page is not getting the variable. I have used $_SESSION, $_GET, $_POST and nothing seems to work. I have even reversed the values in the href line and still nothing works. I used session_start here also.
this is page 2
$id = $_POST['id_num'];
// send query
$sql = 'delete FROM `delegate` WHERE `id_num`= $id';
Your comments would be most appreciated.
You are using GET request for passing the data via link
but in your second page, you are using POST
change it to $id = $_GET['id_num']; and try
and dont use spaces in href "
change it to "
space will be counted as a character
I've often seen assignments to variables of the form "let s.='something'" Here's the specific piece of code in a vim script that I've been struggling to understand:
let s .= '%' . i . 'T'
let s .= (i == t ? '%1*' : '%2*')
let s .= ' '
let s .= i . ':'
let s .= winnr . '/' . tabpagewinnr(i,'$')
let s .= ' %*'
let s .= (i == t ? '%#TabLineSel#' : '%#TabLine#')
The code adds the tab number (i) and viewport number (winnr of tabpagewinnr(i,'$')) to the tab name, so that it looks something like "1: 2/4 Buffer name". From the looks of it, the .= operation seems to be appending stuff to s. But then, I don't understand what the first two lines do. Any help is appreciated.
vim's online help is your friend:
:h .=
:let {var} .= {expr1} Like ":let {var} = {var} . {expr1}".
:h expr-.
expr6 . expr6 .. String concatenation
:h expr1 (well - this is a little hard to find):
expr2 ? expr1 : expr1
The expression before the '?' is evaluated to a number. If it evaluates to TRUE, the result is the value of the expression between the '?' and ':', otherwise the result is the value of the expression after the ':'.
Example:
:echo lnum == 1 ? "top" : lnum
Since the first expression is an "expr2", it cannot contain another ?:. The
other two expressions can, thus allow for recursive use of ?:.
Example:
:echo lnum == 1 ? "top" : lnum == 1000 ? "last" : lnum
To keep this readable, using |line-continuation| is suggested:
:echo lnum == 1
:\ ? "top"
:\ : lnum == 1000
:\ ? "last"
:\ : lnum
You should always put a space before the ':', otherwise it can be mistaken for
use in a variable such as "a:1".
One at a time:
let s .= '%' . i . 'T'
Assuming i=9 and s="bleah", s will now be "bleah%9T"
let s .= (i == t ? '%1*' : '%2*')
This is the familiar ternary operator from C. If t==9, then s is now "bleah%9T%1*". If t is anything but 9, then s is now "bleah%9T%2*"
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++;
}