Why can't I say print $somehash{$var}{fh} "foo"? - perl

I have a line of code along the lines of:
print $somehash{$var}{fh} "foo";
The hash contains the filehandle a few levels down. The error is:
String found where operator expected at test.pl line 10, near "} "foo""
I can fix it by doing this:
my $fh = $somehash{$var}{fh};
print $fh "foo";
...but is there a one-liner?

see http://perldoc.perl.org/functions/print.html
Note that if you're storing
FILEHANDLEs in an array, or if you're
using any other expression more
complex than a scalar variable to
retrieve it, you will have to use a
block returning the filehandle value
instead: ...
So, in your case, you would use a block like this:
print { $somehash{$var}{fh} } "foo";

If you have anything other than a simple scalar as your filehandle, you need to wrap the reference holding the filehandle in braces so Perl knows how to parse the statement:
print { $somehash{$var}{fh} } $foo;
Part of Perl Best Practices says to always wrap filehandles in braces just for this reason, although I don't get that nutty with it.
The syntax is odd because print is an indirect method on a filehandle object:
method_name Object #arguments;
You might have seen this in old-school CGI.pm. Here are two indirect method calls:
use CGI;
my $cgi_object = new CGI 'cat=Buster&bird=nightengale';
my $value = param $cgi_object 'bird';
print "Indirect value is $value\n";
That almost works fine (see Schwern's answer about the ambiguity) as long as the object is in a simple scalar. However, if I put the $cgi_object in a hash, I get the same syntax error you got with print. I can put the braces around the hash access to make it work out. Continuing with the previous code:
my %hash;
$hash{animals}{cgi} = $cgi_object;
# $value = param $hash{animals}{cgi} 'cat'; # syntax error
$value = param { $hash{animals}{cgi} } 'cat';
print "Braced value is $value\n";
That's all a bit clunky so just use the arrow notation for everything instead:
my $cgi_object = CGI->new( ... );
$cgi_object->param( ... );
$hash{animals}{cgi}->param( ... );
You can do the same with filehandles, although you have to use the IO::Handle module to make it all work out:
use IO::Handle;
STDOUT->print( 'Hello World' );
open my( $fh ), ">", $filename or die ...;
$fh->print( ... );
$hash{animals}{fh} = $fh;
$hash{animals}{fh}->print( ... );

The above answers are all correct. The reason they don't allow a full expression in there is print FH LIST is already pretty weird syntax. To put anything more complicated in there would introduce a ton of ambiguous syntax. The block removed that ambiguity.
To see where this madness leads to, consider the horror that is indirect object syntax.
foo $bar; # Is that foo($bar) or $bar->foo()? Good luck!

Related

using file handle returned by select

I am pulling out my hair on using the file handle returned by select.
The documentation about select reads:
select
Returns the currently selected filehandle.
I have a piece of code, that prints some data and usually is executed without any re-direction. But there is one use case, where select is used to re-direct the print output to a file.
In this piece of code, I need to use the current selected file handle. I tried the following code fragment:
my $fh = select;
print $fh "test\n";
I wrote a short test program to demonstrate my problem:
#!/usr/bin/perl
use strict;
use warnings;
sub test
{
my $fh=select;
print $fh "#_\n";
}
my $oldfh;
# this works :-)
open my $test1, "> test1.txt";
$oldfh = select $test1;
test("test1");
close select $oldfh if defined $oldfh;
#this doesn't work. :-(
# Can't use string ("main::TEST2") as a symbol ref while "strict refs" in use
open TEST2,">test2.txt";
$oldfh = select TEST2;
test("test2");
close select $oldfh if defined $oldfh;
#this doesn't work, too. :-(
# gives Can't use string ("main::STDOUT") as a symbol ref while "strict refs" in use at
test("test");
It seems, that select is not returning a reference to the file handle but a string containing the name of the file handle.
What do I have to do to always get a usable file handle from select's return value?
P.S. I need to pass this file handle as OutputFile to XML::Simple's XMLout().
Just use
print XMLout(...);
It seems, that select is not returning a reference to the file handle but a string containing the name of the file handle.
It can indeed return a plain ordinary string.
>perl -MDevel::Peek -E"Dump(select())"
SV = PV(0x6cbe38) at 0x260e850
REFCNT = 1
FLAGS = (PADTMP,POK,pPOK)
PV = 0x261ce48 "main::STDOUT"\0
CUR = 12
LEN = 24
But that's perfectly acceptable as a file handle to Perl. There are four things that Perl accepts as file handles:
A reference to an IO object.
>perl -e"my $fh = *STDOUT{IO}; CORE::say($fh 'foo');"
foo
A glob that contains a reference to an IO object.
>perl -e"my $fh = *STDOUT; CORE::say($fh 'foo');"
foo
A reference to a glob that contains a reference to an IO object.
>perl -e"my $fh = \*STDOUT; CORE::say($fh 'foo');"
foo
A "symbolic reference" to a glob that contains a reference to an IO object.
>perl -e"my $fh = 'STDOUT'; CORE::say($fh 'foo');"
foo
This type doesn't work under strict refs, though.
>perl -Mstrict -e"my $fh = 'STDOUT'; CORE::say($fh 'foo');"
Can't use string ("STDOUT") as a symbol ref while "strict refs" in use at -e line 1.
What do I have to do to always get a usable file handle from select's return value?
As demonstrated above, it already returns a perfectly usable file handle. If XMLout doesn't support it, then it's a bug in XMLout. You could work around it as follows:
my $fh = select();
if (!ref($fh) && ref(\$fh) ne 'GLOB') {
no strict qw( refs );
$fh = \*$fh;
}
This can also be used to make the handle usable in a strict environment
As bad as XML::Simple is at reading XML, it's a million times worse at generating it. See Why is XML::Simple Discouraged?.
Consider XML::LibXML or XML::Twig if you're modifying XML.
Consider XML::Writer if you're generating XML.
The point of select is you don't need to specify the handle at all, since it's the default one.
sub test {
print "#_\n";
}
That's also the reason why select isn't recommended: it introduces global state which is hard to track and debug.
First of all, you shouldn't use XML::Simple , because it will need lots of work to make sure that your output will generate consistent XML. At least make sure you're using the appropriate ForceArray parameters.
Instead of doing filehandle shenanigans, why don't you use the simpler
print XMLout($data, %options);
... instead of trying to pass a default filehandle around?

Perl syntax error printing list index

I am trying to print an index of a list returned from a function call. I am wrapping the function call in parenthesis in an attempt to convert the list to an array. The program fails to compile saying 'syntax error at file.pl line 4, near ")["'. If I create a temporary variable $a or use printf indexing is fine. Why does print brake, is there a better alternative?
sub get{
return (1,2);
}
#print (get())[0]; #fails
printf("%d",(get())[0]);
my $a = (get())[0];
print $a;
I assume that you are foolishly not using
use warnings;
Which is quite a bad thing to not do. If you had used it, you would get the warning:
print (...) interpreted as function
Which is to say, what you wrote
print (get())[0]
Is something Perl interprets as you trying to put a subscript on the print() function. This does not work.
For the code to do what you expect, you need to be explicit about the parentheses:
print ( (get())[0] );
You should always use
use strict;
use warnings;
There is a slight learning curve to using these, but they only show you your errors. Not using them only hides your errors, and your code does not work better.
Try this :
sub get{
return (1,2);
}
print((get)[0]);
or
my $a = (get)[0];
print $a;
OUTPUT
1

Why do I get a syntax error in my compound if statement?

Why do I get a syntax error in the following script?
print "Enter Sequence:";
$a = <STDIN>;
if ($a=="A")|| ($a== "T")|| ( $a == "C")|| ($a== "G")
{
print $a;
}
else
{
print "Error";
}
First, you have a syntax error: The condition expression of an if statement must be in parens.
The second error is found by using use strict; use warnings;, something you should always do. The error is the use of numerical comparison (==) where string comparison (eq) is called for.
The final problem is that $a will almost surely contain a string ending with a newline, so a chomp is in order.
The immediate problem is that he entire logical expression for an if must be in parentheses.
In addition
You must use eq instead of == for comparing strings
Your input string will have a trailing newline, so it will look like "C\n" and will not match a simple one-character string. You need to chomp the input before you compare it
It is generally better to read from STDIN using <> rather than <STDIN>. That way you can specify an input file on the command line, or read from the STDIN if no input was provided
You must always put use strict and use warnings at the top of your program. That will catch many simple errors that you may otherwise overlook
You shouldn't use $a as a variable name. It is a symbol reserved by Perl itself, and says nothing about the purpose of the variable
It is best to use a regular expression for simple comparisons like this. It makes your code much easier to read and will usually make the execution very much faster
Please take a look at this program, which I think does what you want.
use strict;
use warnings;
print "Enter Sequence: ";
my $input = <>;
chomp $input;
if ( $input =~ /^[ATCG]$/i ) {
print $input, "\n";
}
else {
print "Error";
}

How Can I Store a File Handle in a Perl Object and how can I access the result?

I wanted to store a file handle in a Perl Object. Here is how I went about it.
sub openFiles {
my $self = shift;
open (my $itemsFile, "<", "items.txt") or die $!;
open (my $nameFile, "<", "FullNames.txt") or die $!;
$self->{itemsFile} = $itemsFile;
$self->{nameFile} = $nameFile;
return $self;
}
Then I'm looking to access some information from one of these files. Here is how I go about it.
sub getItemDescription {
my $self = #_;
chomp(my $record = $self->{itemsFile});
return $record;
}
I attempt to access it in another procedure as follows:
print "Test 3: $self->getItemDescription()\n";
My questions are as follows:
Is the way I'm saving the file handle in the object correct? If not, how is it wrong?
Is the way I'm reading the lines of the file correct? If not, how can I get it right?
Finally, is the way I'm printing the returned object correct?
This is really important to me. If there is any way that I can improve the structure of my code, i.e. making a global variable for file handling or changing the structure of the object, please let me know.
Is the way I'm saving the file handle in the object correct?
Yes.
Is the way I'm reading the lines of the file correct?
No. That just assigns the file handle. One reads a line from the file using the readline operator.
One would normally use the <...> syntax of the readline operator, but <...> is a shortcut for both readline(...) and glob(qq<...>), and Perl thinks <$self->{itemsFile}> is short for glob(qq<$self->{itemsFile}>). You have to use readline specifically
my $record = readline($self->{itemsFile});
chomp($record) if defined($record);
or do some extra work
my $fh = $self->{itemsFile};
my $record = <$fh>;
chomp($record) if defined($record);
(Note that I don't call chomp unconditionally since readline/<> can return undef.)
Finally, is the way I'm printing the returned object correct?
I presume you mean returned string, as in the string returned by getItemDescription. The catch is, you never actually call the method. ->getItemDescription() has no meaning in double quoted string literals, even after a variable. You need to move $self->getItemDescription() out of the double quotes.
You also fail to check if you've reached the end of the file.
You are close.
To read a record (line) from a filehandle, you use the builtin readline function or the <...> operator AFTER you assign the filehandle to a "simple scalar" (see edit below).
chomp(my $record = readline( $self->{itemsFile} );
my $fh = $self->{itemsFile};
chomp(my $record = <$fh>);
There is also a bug in your getItemDescription method. You'll want to say
my ($self) = #_;
instead of
my $self = #_;
The latter call is a scalar assignment of an array, which resolves to the length of the array, not the first element of the array.
EDIT: <$self->{itemsFile}> and <{$self->{itemsFile}}> do not work, as perlop explains:
If what's within the angle brackets is neither a filehandle nor a simple scalar variable containing a filehandle name, typeglob, or typeglob reference, it is interpreted as a filename pattern to be globbed, and either a list of filenames or the next filename in the list is returned, depending on context. This distinction is determined on syntactic grounds alone. That means <$x> is always a readline() from an indirect handle, but <$hash{key}> is always a glob(). That's because $x is a simple scalar variable, but $hash{key} is not--it's a hash element. Even <$x > (note the extra space) is treated as glob("$x "), not readline($x).
The openFiles piece is correct.
The errors occur primarily getItemDescription method.
First as previously mentioned my $self = #_; should be my ($self) = #_;.
However, the crux of the question is solved in the following fashion:
Change chomp(my $record = $self->{itemsFile}); to two lines:
$file1 = $self->{itemsFile};
chomp(my $record = $file1);
To clarify you must (in my experience and I tried all the solutions suggested) use a scalar value.
Finally, see the last two paragraphs in ikagami's answer.

Use variadic arguments as arguments for sprintf

I'd like to program a wrapper around printf(...).
My first attempt was:
sub printf2 {
my $test = sprintf(#_);
print $test;
}
As the array (in scalar context) isn't a format string, this doesn't work (as expected).
Does anyone know a solution? Probably without using any special packages?
EDIT: In the real context, I'd like to use sprintf. Apparently there is a difference between printf and sprintf.
The sprintf function has a ($#) prototype, so the first argument to sprintf is always evaluated in scalar context, even if it is an array.
$x = sprintf(#a); # same as sprintf(scalar #a)
So before you call sprintf, you need to separate the template from the rest of the arguments.
Here's a concise way:
sub printf2 {
my $test = sprintf(shift, #_);
print $test;
}
Curiously, printf doesn't have a prototype and does what you expect.
printf(#a); # same as printf($a[0], #a[1..$#a])
How about this
sub pf { printf $_[0],#_[1..$#_] }
Many of the named operators (such as sprintf) have special syntaxes. sprintf's syntax is defined to be
sprintf FORMAT, LIST
This can often (but not always) be seen using prototype.
>perl -wE"say prototype 'CORE::sprintf'"
$#
The problem is that you used one of the following syntaxes instead of the documented syntax.
sprintf ARRAY
sprintf LIST
Simply switch to the documented syntax to solve your problem.
sub printf2 {
my ($format, #args) = #_;
print sprintf($format, #args);
}
Or if you want to avoid the copying,
sub printf2 {
print sprintf($_[0], #_[ 1..$#_ ]);
}