trying to return a hash from a perl function and getting "can't use string ("0") as a HASH ref...." - perl

I have a Perl function (named readDicomFile) which ends thusly:
return { 'fileProperties' => \%fileProperties, 'filehandle' => *FH, 'buffersize' => $buffersize };
the code that calls it looks like this:
$file = readDicomFile( $ARGV[0] );
#use Data::Dumper;
#print Dumper $file;
my #imageData;
local *FH = $file->{filehandle};
while ( read(FH, $_, $file->{buffersize}) ) {
push #imageData, unpack( 'S' x ( $file->{buffersize}/($file->{fileProperties}->{bitsAllocated}/8) ), $_ );
}
print "DEBUG: found ", (scalar #imageData), " elements\n";
I get this output:
Can't use string ("0") as a HASH ref while "strict refs" in use at ./test.pl line 17.
DEBUG: found 262156 elements
When I try to figure out what's happening with my data structure, I uncomment the two lines for using Data::Dumper and I get this:
$VAR1 = {
'fileProperties' => {
'echoNumber' => '',
'highBit' => 11,
'rows' => 512,
'bitsAllocated' => 16,
'modality' => 'CT',
'echoTime' => '',
'windowCenter' => '200',
'studyDescription' => 'CT SINUS / FACIAL WITH CONTRAST ',
'repetitionTime' => '',
'sequenceName' => '',
'method' => 'perl method',
'seriesNumber' => '502 ',
'imageNumber' => '0 ',
'windowWidth' => '50',
'trailer' => 0,
'pixelRepresentation' => 0,
'sliceLocation' => '',
'bitsStored' => 12,
'ultrasoundColorData' => '',
'rescaleIntercept' => 0,
'photometricInterpretation' => 'MONOCHROME2 ',
'description' => 'Patient Protocol',
'imageDataType' => '',
'imagePosition' => '',
'columns' => 512,
'studyDate' => '20140505'
},
'filehandle' => *Radiology::Images::FH,
'buffersize' => '1024'
};
I've played around with several different idioms for returning the hash values from the function (like passing back a hash or a hashref), but I'm always getting the same error.
Does anyone have insight into my problem?
Thanks in advance.
EDIT:
I've been playing around with this all afternoon. Here is the entirety of the current test.pl
#!/usr/bin/perl -w
use lib '/etc/perl';
use strict;
use Radiology::Images;
my $file = $ARGV[0];
$file = readDicomFile( $file );
print STDERR "DEBUG: $file->{fileProperties}->{bitsAllocated}\n";
my #imageData;
# while ( $readsize = read ( $file->{filehandle}, $_, $file->{buffersize} ) ) {
# push #imageData, unpack( 'S' x ( $file->{buffersize}/($file->{fileProperties}->{bitsAllocated}/8) ), $_ );
# }
my $readsize;
my $imagesize = $file->{fileProperties}->{columns} * $file->{fileProperties}->{rows};
print "DEBUG: should find $imagesize elements\n";
while ( $imagesize > 0 ) {
$readsize = read ( $file->{filehandle}, $_, $file->{buffersize} );
push #imageData, unpack( 'S' x ( $readsize/( $file->{fileProperties}->{bitsAllocated}/8 ) ), $_ );
$imagesize -= $readsize /( $file->{fileProperties}->{bitsAllocated}/8 );
}
print "DEBUG: found ", (scalar #imageData), " elements\n";
...which gives me this output
DEBUG: 16
DEBUG: should find 262144 elements
Can't use string ("0") as a HASH ref while "strict refs" in use at /root/test.pl line 7.
DEBUG: found 262144 elements
...HOWEVER, when I change the 'while' loop in line #15 to
while ( $imagesize > 34816 ) {
I get this output:
DEBUG: 16
DEBUG: should find 262144 elements
DEBUG: found 227328 elements
So, it would appear that something I'm doing in a loop between lines 15 and 18 causes an error that goes back in time to line 7. Thus, my problem has never been with passing the hash back from my function. ??
BTW, the number 34816 was arrived experimentally. 34816 doesn't trigger the error, 34815 does.
Given this is looking completely whacky and given that the code works the way I think it is supposed to despite the error, I guess I'll just assume it is a language bug and turn my attention to just suppressing the error message.
2nd EDIT:
This is test.pl now:
#!/usr/bin/perl -w
use lib '/etc/perl';
use strict;
use Radiology::Images;
my $file = $ARGV[0];
$file = readDicomFile( $file );
my #imageData;
my $readsize;
while ( $readsize = read ( $file->{filehandle}, $_, $file->{buffersize} ) ) {
push #imageData, unpack( 'S' x ( $readsize/($file->{fileProperties}->{bitsAllocated}/8) ), $_ );
}
print "DEBUG: found ", (scalar #imageData), " elements\n";
gives me this output:
Can't use string ("0") as a HASH ref while "strict refs" in use at /root/test.pl line 9.
DEBUG: found 5638203 elements
if I comment out the 'use strict' line, I get:
Use of uninitialized value in ref-to-glob cast at /root/test.pl line 9.
Use of uninitialized value in read at /root/test.pl line 9.
read() on unopened filehandle at /root/test.pl line 9.
DEBUG: found 5638215 elements
I, uh, obviously...don't understand this....

You need to return a reference to your file handle:
return {
'fileProperties' => \%fileProperties,
'filehandle' => \*FH, # <--- reference.
'buffersize' => $buffersize,
};
And then when reading, you can work directly on the filehandle, you don't have to transfer it to a fileglob:
# local *FH = $file->{filehandle}; <--- Not Needed. Below, just use the lexical fh
while ( read($file->{filehandle}, $_, $file->{buffersize}) ) {
If you work with lexical file handles from the very beginning, it becomes a lot more obvious how to pass them:
open my $fh, '<', 'myfile.txt' or die "Can't open: $!";
return {
'fileProperties' => \%fileProperties,
'filehandle' => $fh,
'buffersize' => $buffersize,
};

Related

Issue in Printing data from a hash table in perl

I am trying to process the data in a single file . i have to read the file and create a hash structure,get the value of fruitname append it to fruitCount and fruitValue and delete the line fruitName and write the entire output after the change is done.Given below is the content of file.
# this is a new file
{
date 14/07/2016
time 11:15
end 11:20
total 30
No "FRUITS"
Fruit_class
{
Name "fruit 1"
fruitName "apple.fru"
fruitId "0"
fruitCount 5
fruitValue 6
}
{
Name "fruit 2"
fruitName "orange.fru"
fruitId "1"
fruitCount 10
fruitValue 20
}
}
I tried with following code :
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %hash_table;
my $name;
my $file = '/tmp/fruitdir/fruit1.txt';
open my $fh, "<", $file or die "Can't open $file: $!";
while (<$fh>) {
chomp;
if (/^\s*fruitName/) {
($name) = /(\".+\")/;
next;
}
s/(fruitCount|fruitValue)/$name\.$1/;
my ($key, $value) = split /\s+/, $_, 2;
$hash_table{$key} = $value;
}
print Dumper(\%hash_table);
This is not working . I need to append the value of fruitname and print the the entire file content as output. Any help will be appreciated.Given below is the output that i got.
$VAR1 = {
'' => undef,
'time' => '11:15 ',
'date' => '14/07/2016',
'{' => undef,
'#' => 'this is a new file',
'total' => '30 ',
'end' => '11:20 ',
'No' => '"FRUITS"',
'Fruit_class' => undef,
'}' => undef
};
Expected hash as output:
$VAR1 = {
'Name' => '"fruit 1"',
'fruitId' => '"0" ',
'"apple_fru".fruitValue' => '6 ',
'"apple_fru".fruitCount' => '5'
'Name' => '"fruit 2"',
'fruitId' => '"0" ',
'"orange_fru".fruitValue' => '10 ',
'"orange_fru".fruitCount' => '20'
};
One word of advice before I continue:
Document your code
There are several logic errors in your code which I think you would have recognized if you wrote down what you thought each line was supposed to do. First, write down the algorithm that you would like to implement, then document how each step in the code implements a step in the algorithm. At the end you'll be able to see what you missed, or what part is not working.
Here are the errors that I see
You aren't ignoring lines that you shouldn't be parsing. For example, you're grabbing the '}' and '{' lines.
You aren't actually storing the name of the fruit. You grab it, but immediately start the next loop without storing it.
You're not keeping track of each structure. You need to start a new structure for each fruit.
Do you really want to keep the double quotes in the values?
Other things to worry about:
Are you guaranteed that the list of attributes is in that order? For example, can Name come last?
Here's some code which does what I think you want.
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %hash_table;
my $name;
my #fruit;
my $file = '/tmp/fruitdir/fruit1.txt';
open my $fh, "<", $file or die "Can't open $file: $!";
while (<$fh>) {
chomp;
# save hash table if there's a close bracket, but
# only if it has been filled
if ( /^\s*}\s*$/ ) {
next unless keys %hash_table;
# save COPY of hash table
push #fruit, { %hash_table };
# clear it out for the next iteration
%hash_table = ();
}
# only parse lines that start with Name or fruit
next unless
my ( $key, $value ) =
/^
# skip any leading spaces
\s*
# parse a line beginning with Name or fruitXXXXX
(
Name
|
fruit[^\s]+
)
# need space between key and value
\s+
# everything that follows is a value. clean up
# double quotes in post processing
(.*)
/x;
# remove double quotes
$value =~ s/"//g;
if ( $key eq 'Name' ) {
$name = $value;
}
else {
$key = "${name}.${key}";
}
$hash_table{$key} = $value;
}
print Dumper \#fruit;
and here's the output:
$VAR1 = [
{
'fruit 1.fruitValue' => '6',
'fruit 1.fruitName' => 'apple.fru',
'Name' => 'fruit 1',
'fruit 1.fruitCount' => '5',
'fruit 1.fruitId' => '0'
},
{
'fruit 2.fruitName' => 'orange.fru',
'fruit 2.fruitId' => '1',
'fruit 2.fruitCount' => '10',
'fruit 2.fruitValue' => '20',
'Name' => 'fruit 2'
}
];

Indexation and searching with Lucy

I am trying to index a folder with text files, and search the files which contain a word using lucy::simple module.
Here is my code so far:
#!/usr/bin/perl
use strict;
use warnings;
use Lucy::Simple;
use Lucy::Search::IndexSearcher;
#make_path($ch_Index);
my $index = Lucy::Simple->new(
path => "/home/akpinar/Desktop/lucyindex/text",
language => 'en',
);
while ( my ( $title, $content ) = each my %source_docs ) {
my $index->add_doc({
title => $title,
content => $content,
});
}
#print "Indexation finie\n";
###requete
print "Veuillez taper votre requĂȘte : \n";
my $requete = <STDIN>;
chomp $requete ;
$requete=lc($requete);
my $nbTrouve = $index->search(
query =>$requete,
offset=>0,
num_wanted=>100,
);
print "Total hits: $nbTrouve\n";
while ( my $hit = $index->next ) {
print "$hit->{title}\n",
}
but i get the error:
Failed to read seg_1
S_try_open_elements at core/Lucy/Index/PolyReader.c line 290
at lucyrequete.pl line 21, <STDIN> line 1.
eval {...} called at lucyrequete.pl line 21
lucy_PolyReader_do_open at core/Lucy/Index/PolyReader.c line 439
at lucyrequete.pl line 21, <STDIN> line 1.
Does anybody know why I get this?
You need to create the path directory first:
mkdir /home/akpinar/Desktop/lucyindex/text
I was confused too because Lucy::Simple and Lucy::Index::Indexer do not give the same api.
path=> for ::Simple versus index=> for ::Indexex.
And, this second may create the directory.
$indexer = Lucy::Index::Indexer->new(
schema => $schema,
index => $outIndex,
create => 1,
truncate =>0,
);

Perl: overwrite structure member value

I am creating $input with this code:
push(#{$input->{$step}},$time);, then I save it in an xml file, and at the next compiling, I read it from that file. When i print it, i get the structure bellow.
if(-e $file)
my $input =XMLin($file...);
print Dumper $input;
and I get this structure
$VAR1 = {
'opt' => {
'step820' => '0',
'step190' => '0',
'step124' => '0',
}
};
for each step with it's time..
push(#{$input->{$step}},$time3);
XmlOut($file, $input);
If I run the program again, I get this structure:
$VAR1 = {
'opt' => {
'step820' => '0',
'step190' => '0',
'step124' => '0',
'opt' => {
'step820' => '0',
'step190' => '0',
'step124' => '0'
}
}
I just need to overwrite the values of steps(ex:$var1->opt->step820 = 2). How can i do that?
I just need to overwrite the values of steps(ex:$var1->opt->step820 = 2). How can i do that?
$input->{opt}->{step820} = 2;
I'm going to say what I always do, whenever someone posts something asking about XML::Simple - and that is that XML::Simple is deceitful - it isn't simple at all.
Why is XML::Simple "Discouraged"?
So - in your example:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $xml= XML::Twig->new->parsefile($file);
$xml -> get_xpath('./opt/step820',0)->set_text("2");
$xml -> print;
The problem is that XML::Simple is only any good for parsing the type of XML that you didn't really need XML for in the first place.
For more simple examples - have you considered using JSON for serialisation? As it more directly reflects the hash/array structure of native perl data types.
That way you can instead:
print {$output_fh} to_json ( $myconfig, {pretty=>1} );
And read it back in:
my $myconfig = from_json ( do { local $/; <$input_fh> });
Something like:
#!/usr/bin/env perl
use strict;
use warnings;
use JSON;
my $input;
my $time = 0;
foreach my $step ( qw ( step820 step190 step124 ) ) {
push(#{$input->{$step}},$time);
}
print to_json ( $input, {pretty=>1} );
Giving resultant JSON of:
{
"step190" : [
0
],
"step820" : [
0
],
"step124" : [
0
]
}
Although actually, I'd probably:
foreach my $step ( qw ( step820 step190 step124 ) ) {
$input->{$step} = $time;
}
print to_json ( $input, {pretty=>1} );
Which gives;
{
"step190" : 0,
"step124" : 0,
"step820" : 0
}
JSON uses very similar conventions to perl - in that {} denote key value pairs (hashes) and [] denote arrays.
Look at the RootName option of XMLout. By default, when "XMLout()" generates XML, the root element will be named 'opt'. This option allows you to specify an alternative name.
Specifying either undef or the empty string for the RootName option will produce XML with no root elements.

perl eval() gives 'Insecure dependency in eval while running with -T switch' on string variable

The variables $var and $var2 in the following code hold same value but behave differently with respect to eval().
Source:
use Data::Dumper;
sub trim($)
{
my $string = shift;
$string =~ s/^\s+//;
$string =~ s/\s+$//;
$string =~ s/\R//g;
return $string;
}
my $var2="";
$var2.="{id=>1962}";
$var2.=",{id=>1645}";
$var2.=",{id=>905}";
$var2.=",{id=>273}";
$var2.=",{id=>1800}";
$var2.=",{id=>21}";
$var2.=",{id=>1639}";
$var2.=",{id=>55}";
$var2.=",{id=>57}";
$var2.=",{id=>59}";
$var2.=",{id=>420}";
$var2.=",{id=>418}";
$var2="[".$var2."]";
print Dumper $var2;
print Dumper eval($var2); #evaluates to an ARRAY
my $filename = "sample.txt";
open(FILE, $filename) or die "Can't read file 'filename' [$!]\n";
$document = <FILE>;
close (FILE);
$document=trim($document);
#data = split(',', $document);
my $var = "";
foreach my $val (#data) {
$var.="{id=>".$val."},";
}
chop($var);
$var = "[".$var."]";
print "\n";
if ($var eq $var2){
print "var and var2 stringwise equal\n" ;
}else{
print "var and var2 stringwise not equal\n" ;
}
print Dumper $var;
print Dumper eval($var); #error
exit(0);
Content of sample.txt:
1962,1645,905,273,1800,21,1639,55,57,59,420,418
Output:
$VAR1 = '[{id=>1962},{id=>1645},{id=>905},{id=>273},{id=>1800},{id=>21},{id=>1639},{id=>55},{id=>57},{id=>59},{id=>420},{id=>418}]';
$VAR1 = [
{
'id' => 1962
},
{
'id' => 1645
},
{
'id' => 905
},
{
'id' => 273
},
{
'id' => 1800
},
{
'id' => 21
},
{
'id' => 1639
},
{
'id' => 55
},
{
'id' => 57
},
{
'id' => 59
},
{
'id' => 420
},
{
'id' => 418
}
];
var and var2 stringwise equal
$VAR1 = '[{id=>1962},{id=>1645},{id=>905},{id=>273},{id=>1800},{id=>21},{id=>1639},{id=>55},{id=>57},{id=>59},{id=>420},{id=>418}]';
Insecure dependency in eval while running with -T switch at assignment.pl line 51.
Can anyone tell why "eval($var)" doesn't get evaluated despite having same value as that of $var2 ?
While $var might be the same as $var2 in your specific case of data, it isn't necessarily always that case. You script also doesn't forbid the eval even if it is not the same.
Thus, the tainted check is right to complain about the insecure eval, as it is intended to detect potentially unsafe operations which your eval($var) definitely is.
Generally, you should try to avoid eval wherever you can, as it is a prime source of remote-code-execution vulnerabilities. Instead, you should try to parse your data structures using other, safer means, e.g. by using split on your input data and then looping over the resulting array to produce your desired data structure.
This is perl Taint Mode doing exactly what it's supposed to be doing. You're reading in data from an external resource, and perl -T is not allowing you to run tainted data through eval since that literally could end up doing anything (very insecure).
In order to launder your data you simply need to run in through a regular expression to verify what it is. Replace the following line:
#my #data = split(',', $document);
my #data = $document =~ m/(\d+)/g;
Because we're running the external document data through a regex, the values in #data will no longer be tainted and can be eval'd.
Either way, I'd advise against using eval at all unless there is a specific reason why you need it. The following accomplishes the same thing without the need for an eval
my $var = [map {id => $_}, #data];

Use of uninitialized value in concatenation (.) or string in slapd_ munin plugin

I'm trying to implement the slapd_ munin plugin which is written in perl which I'm pretty much clueless about. The full plugin is available here. The error I'm getting is this one:
Use of uninitialized value in concatenation (.) or string at
/etc/munin/plugins/slapd_localhost line 232, <DATA> line 275.
Line 232 is this one:
my $searchdn = $ops{$action}->{'search'} . "," . $basedn;
I tried debugging by outputing all the variables/objects as follows:
use Data::Dumper; # top of script
# [...]
print Dumper(%ops);
print "action = [$action]\n";
print "basedn = [$basedn]\n\n";
my $searchdn = $ops{$action}->{'search'} . "," . $basedn;
When I run it again here is what I obtain:
[...] # 15 other variables belonging to $ops
$VAR16 = {
'info' => 'The graph shows the number of Waiters',
'search' => 'cn=Waiters',
'desc' => 'The current number of Waiters',
'filter' => '(|(cn=Write)(cn=Read))',
'title' => 'Number of Waiters',
'label2' => {
'read' => 'Read',
'write' => 'Write'
},
'vlabel' => 'Waiters'
};
action = [localhost]
action = [cn=Monitor]
Use of uninitialized value in concatenation (.) or string at /etc/munin/plugins/slapd_localhost line 237, <DATA> line 275.
Since all the variables seem to be set, I really don't understand the error message I'm getting
Q: Can anybody advise on how debugging this script?
You should dump a reference to %ops, as in
print Dumper \%ops;
This will make the debug output clearer. To illustrate, consider the output of
#! /usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %h = (foo => { bar => 1 }, baz => { quux => 3 });
print "No reference:\n",
Dumper(%h),
"\n",
"Reference:\n",
Dumper(\%h);
Notice how you see the structure much more clearly in the latter half:
No reference:
$VAR1 = 'baz';
$VAR2 = {
'quux' => 3
};
$VAR3 = 'foo';
$VAR4 = {
'bar' => 1
};
Reference:
$VAR1 = {
'baz' => {
'quux' => 3
},
'foo' => {
'bar' => 1
}
};
You cut out a critical bit of the output. What's the value of $VAR15? Is it "localhost" or something else?
When you print $searchdn, what is its value?