Concatenating strings with spaces in Perl. Any built-ins? - perl

In Perl I can concatenate multiple strings with spaces between them as follows:
my $long_string = $one_string . " " . $another_string . " " . $yet_another_string . " " .
$and_another_string . " " $the_lastr_string
However, typing this is a bit cumbersome.
Is there a built-in that can make this task easier?
e.g. something like:
concatenate_with_spaces($one_string, $another_string, $yet_another_string, ...)

You want join:
my $x = 'X';
my #vars = ( 1, 'then', 'some' );
my $long_string = join ' ', $x, 2, #vars; # "X 2 1 then some"

Zaid has given the idiomatic solution, using join. However, there are more ways to do it.
my #vars = ($one, $two, $three);
my $str1 = "#vars"; # Using array interpolation
my $str2 = "$one $two $three"; # interpolating scalars directly
Interpolating an array uses the predefined variable $" (list separator), which is by default set to space. When interpolating variables, you do not need to use . to concatenate spaces to your strings, they can be used directly in a double quoted string.

my #list_of_strings = ($one_string, $two_strings );
my $string = join(' ', #list_of_strings );
print $string;

Related

Perl: What is this + doing here? (print plus) [duplicate]

This question already has answers here:
Why is 'print (52-80)*42' different than 'print 42*(52-80)' in Perl?
(3 answers)
Closed 7 years ago.
Recently I came across a statement like this
print +( map { $_ + 1 } #$_ ), "\n" for #$array; # <- AoA
I had not seen the + operator used with print like this before. Running the statement, it is not hard to infer what it is doing. But I am having trouble finding documentation on the + operator used this way.
my #tab = (
[1,2,3,4],
[qw(a b c d)],
);
my $test = "(", +( map { qq( >>$_<< ) } #$_ ), ")\n" for #tab;
print $test;
my #test = "(", +( map { qq( >>$_<< ) } #$_ ), ")\n" for #tab;
print #test;
my %test = "(", +( map { qq( >>$_<< ) } #$_ ), ")\n" for #tab;
print %test;
Above produces the warnings: Useless use of a constant ()) in void context for all three tests. Use of uninitialized value for the scalar test and Odd number of elements in hash assignment for both the array and hash tests, It would be nice to be able to store the statements output in a scalar. I know I can store an Identical string in a scalar using a for loop like below.
print "(", +( map { qq( >>$_<< ) } #$_ ), ")\n" for #tab;
my $out = '';
for (#tab) {
$out .= "(" . ( join '', map { qq( >>$_<< ) } #$_ ) . ")\n";
}
print $out;
# output ...
( >>1<< >>2<< >>3<< >>4<< )
( >>a<< >>b<< >>c<< >>d<< )
---
( >>1<< >>2<< >>3<< >>4<< )
( >>a<< >>b<< >>c<< >>d<< )
Why can't I store the statement in any variable or test its type. Id like in detail what is actually happening with the print builtin when using the + operator.
EDIT: I belive my original post was kind of confusing, I did want to learn more about the + operator used with print but what I was really after is how to store each iteration of the shorthand for statement into a scalar string - I found a way after some testing ...
use strict;
use warnings;
my #a = ([1,2,3],[qw/a b c/]);
my $one = '';
$one .= "(" . ( join '', map { " >>$_<< " } #$_ ) . ")\n" for #a;
print $one;
# output ...
( >>1<< >>2<< >>3<< )
( >>a<< >>b<< >>c<< )
I think you've asked two questions here, so I'll answer both of them as well as I can...
Firstly, you're referring to the unary "+" operator, from perlop
Unary "+" has no effect whatsoever, even on strings. It is useful syntactically for separating a function name from a parenthesized expression that would otherwise be interpreted as the complete list of function arguments.
To expand on what exactly is going on in the print statement, I found the explanation on perlmaven to be pretty good.
The documentation explains that the + separates the print function from the parentheses ( and tells perl that these are not the parentheses wrapping the parameters of the print function.
That might satisfy you, but if you are further intersted you can use the B::Deparse module to ask perl how does it understand this code-snippet:
print +(stat $filename)[7];
We save that content in the plus.pl file and run perl -MO=Deparse plus.pl. The result is:
print((stat $filename)[7]);
Hope this is helpful!
Edit
Explaining in detail why you can't use the same command to print that sequence and assign it to a scalar is maybe outside of the scope of this question, but I'll attempt to briefly explain... with the note that if I'm wrong about something, I'd love a correction, I've come to understand how this works through writing code using this, not from reading the perl docs on its inner workings :)
With the print statement
print "(", +( map { qq( >>$_<< ) } #$_ ), ")\n" for #tab;
perl is interpreting it as
(print "(", +( map { qq( >>$_<< ) } #$_ ), ")\n") for #tab;
That is, it's grouping all of the stuff you want to print, and performing it for each element in #tab. However, with your scalar assignment
my $test = "(", +( map { qq( >>$_<< ) } #$_ ), ")\n" for #tab;
perl is performing the for #tab code on ")\n", thus your Useless use of a constant ()) in void context warning. To do what you want, you need to make it explicit what you want for'd
my $test;
( $test .= "(" . join ('', ( map { qq( >>$_<< ) } #$_)) . ")\n") for #tab;
print $test;
Should get you what you're looking for. Note the parenthesis around the entire assignment, and the omission of the unary "+" before the map, which is no longer necessary.
perldoc -f print:
Be careful not to follow the
print keyword with a left parenthesis unless you want the
corresponding right parenthesis to terminate the arguments to the
print; put parentheses around all arguments (or interpose a "+",
but that doesn't look as good).
In this case, you do not want map to consume the "\n", you must put parentheses around the map { $_ + 1 } #$_. But, the moment you do that you run into the situation described above unless you put a + before the left parenthesis so that +(map { $_ + 1 } #$_) becomes a single argument to print instead of (map { $_ + 1 } #$_) being interpreted as the complete argument list.
If you had
print ( map { $_ + 1 } #$_ ), "\n" for #$array;
the comma following the right parenthesis above would be interpreted as the comma operator. print would just print the numbers, and not the newline:
$ perl -we '$array=[[1,2,3]];print ( map { $_ + 1 } #$_ ), "\n" for #$array;'
print (...) interpreted as function at -e line 1.
Useless use of a constant ("\n") in void context at -e line 1.
234%
The print is evaluated. As a side effect, it prints the numbers. The return value of the print is discarded, and the value of the expression becomes "\n". Since this value is not assigned to anything, you get the warning. Basically, the code ends up looking like:
"\n" for #$array;
except for the side effect of invoking print.
If you want to store the output of the loop in a variable, you can do:
my $string;
$string .= sprintf( "(%s)\n", join('', map {$_ + 1} #$_) ) for #tab;

Shift and Split

i want to make this 2-Commands shorter, 1 line if it possible.
my $id = shift;
my #splittedID = split(" ", $id);
return $splittedID[0];
but it should have at the end the same functional. Thanks
return (split " ", shift)[0];
Or, if you want:
(split " ", shift)[0];
(The result of the last line of a sub implicitly becomes the return value).
Even shorter (Perl 5.16 required for /r option):
$_[0]=~s/ .*//r
Of course, in actual production code, your original example is better, since it's readable.
Because you just want the first item of the split and ' ' is a special pattern that skips all leading spaces, a regex can also solve this like so:
sub firstword {
return (shift =~ /(\S+)/)[0];
}
my $x = firstword('asdf qwer jkl') # Equals 'asdf';
my $y = firstword(' qwer jkl') # Equals 'qwer';
my $z = firstword(' ') # Equals undef;
Also, the return keyword is optional of course, but shorter is not always better.
This is much more compact using a regular expression:
sub first_field {
return unless $_[0] =~ /(\S+)/;
$1;
}

stripping off numbers and alphabetics in perl

I have an input variable, say $a. $a can be either number or string or mix of both.
My question is how can I strip off the variable to separate numeric digits and alphabetic characters?
Example;
$a can be 'AB9'
Here I should be able to store 'AB' in one variable and '9' in other.
How can I do that?
Check this version, it works with 1 or more numeric and alphabetic characters in a variable.
#!/usr/bin/perl
use strict;
use Data::Dumper;
my $var = '11a';
my (#digits, #alphabetics);
while ($var =~ /([a-zA-Z]+)/g) {
push #alphabetics, $1;
}
while ($var =~ /(\d+)/g) {
push #digits, $1;
}
print Dumper(\#alphabetics);
print Dumper(\#digits);
Here's one way to express it very shortly:
my ($digits) = $input =~ /(\d+)/;
my ($alpha) = $input =~ /([a-z]+)/i;
say 'digits: ' . ($digits // 'none');
say 'non-digits: ' . ($alpha // 'none');
It's important to use the match operator in list context here, otherwise it would return if the match succeeded.
If you want to get all occurrences in the input string, simply change the scalar variables in list context to proper arrays:
my #digits = $input =~ /(\d+)/g;
my #alpha = $input =~ /([a-z]+)/gi;
say 'digits: ' . join ', ' => #digits;
say 'non-digits: ' . join ', ' => #alpha;
For my $input = '42AB17C', the output is
digits: 42, 17
non-digits: AB, C

perl substr to get a substring

#!/usr/bin/perl
my $str = "abc def yyy ghi";
print substr($str, 0 , index($str,' '));
I want substr to print def yyy
print substr ($str, index ($str, ' '), rindex($str, ' ') does not work?
Any idea?
You didn't specify EXACTLY what you want as far as logic but the best guess is you want to print characters between first and last spaces.
Your example code would print too many characters as it prints # of characters before the last space (in your example, 10 instead of 7). To fix, you need to adjust the # of characters printed by subtracting the # of characters before the first space.
Also, you need to start one character to the right of your "index" value to avoid printing the first space - this "+1" and "-1" in the example below
$cat d:\scripts\test1.pl
my $str = "abc def yyy ghi";
my $first_space_index = index ($str, ' ');
my $substring = substr($str, $first_space_index + 1,
rindex($str, ' ') - $first_space_index - 1);
print "|$substring|\n";
test1.pl
|def yyy|
The third argument is length, not offset. But it can be negative to indicate chars from the end of the string, which is easily gotten from rindex and length, like so:
my $str = "abc def yyy ghi";
print substr( $str, 1 + index( $str, ' ' ), rindex( $str, ' ' ) - length($str) );
(Note adding 1 to get the offset after the first space.)
If you want to print text between first and last space, wouldn't it be easier with regex?
print $1 if "abc def yyy ghi" =~ / (.*) /
frankly, substr/index/rindex are really not the way to go there. You are better off doing something like:
my $str = "abc def yyy ghi";
my #row = split ' ', $str;
pop #row and shift #row;
print "#row";
Which is more inefficient, but captures the actual intent better

Appending a prefix when using join in Perl

I have an array of strings that I would like to use the join function on. However, I would like to prefix each string with the same string. Can I do this in one line as opposed to iterating through the array first and changing each value before using join?
Actually it's a lil bit trickier. The prefix is not part of the join separator. Meaning if you used a prefix like "num-" on an array of (1,2,3,4,5), you will want to get this result: num-1,num-2,num-3,num-4,num-5
This code:
my #tmp = qw(1 2 3 4 5);
my $prefix = 'num-';
print join "\n", map { $prefix . $_ } #tmp;
gives:
num-1
num-2
num-3
num-4
num-5
Just make the prefix part of the join:
my #array = qw(a b c d);
my $sep = ",";
my $prefix = "PREFIX-";
my $str = $prefix . join("$sep$prefix", #array);
You could also use map to do the prefixing if you prefer:
my $str = join($sep, map "$prefix$_", #array);