Use of uninitialized value in pattern match - regex

I have the following code in a subroutine in Perl for which I keep getting the following error :
Use of uninitialized value $nextLine in pattern match (m//) at catlist.pl line 67, line 2756.
sub extract_testdesc {
my #str = #_;
my $file = $_[0];
my $testname = $_[1];
my #fifo;
# Open the file
open( FILEHANDLE, $file ) or die "couldnt open";
while (<FILEHANDLE>) {
if ( $_ =~ m/\/\*\*/ ) { # if start of comment /**
undef(#fifo);
$nextLine = <FILEHANDLE>;
while ( $nextLine !~ m/\*\// ) { # Add all lines into array until */ is encountered
if ( $nextLine !~ m/\#testlogic.author/ ) {
$nextLine =~ s/\*//g;
if ( $nextLine ne "" ) {
push( #fifo, $nextLine );
}
}
$nextLine = <FILEHANDLE>;
}
}
if ( $_ =~ m/$testname/ ) {
return (#fifo);
}
}
close(FILEHANDLE);
}
What am I doing wrong ? I'm new to Perl so any help is appreciated.

Whenever you use a while loop on a file handle, it's actually synonymous with while (defined($_ = <FILEHANDLE>)) {. This is useful because once the filehandle reaches the eof, it will exit the loop. On the other hand, you are doing manual calls readline calls without testing to see if anything was returned, hence your uninitialized value warnings.
Overall, your goal and logic are confusing. However, perhaps an introduction to the range operator will help you? The following achieves what I think you logic is, but I easily could have misinterpreted.
sub extract_testdesc {
my ($file, $testname) = #_;
my #fifo;
# Open the file
open my $fh, '<', $file or die "couldnt open: $!";
while (<$fh>) {
if ( my $range = m{\Q/**} .. m{\Q*/}) {
#fifo = () if $range == 1;
push #fifo, $_;
} elsif ($_ =~ m/\Q$testname\E/ ) {
return (#fifo);
}
}
close($fh);
}

Related

Perl: Comparing the contents of one file with those of several others

I have to read a CSV file (TEST.csv) with contents of this sort:
Sl.No, Label, Customer1, Customer2, Customer3...
1, label1, Y, N, Y...
2, label2, N, Y, Y...
...
and retrieve only the labels marked as "Y" for every "customer", into an external file for that "customer". With some help from SO members in another question, I managed to desist from getting lost in a maze of nested data structures, and am using the construct below. Here, I'm copying the labels marked as "Y" to a temp file _temp.h for the corresponding customers.
Now, the actual "external file" I need to write does not just have the labels, but is a copy of an "internal file" internal.h, which has data in this form:
/*...comments*/
#define header_label1 val1;
#define header_label2 val2;
...
For example, I might have a line #define ABC_Comp1_X_H_CompDes1 value. If the label Comp1_CompDes1 is present in the temp file I create for customer 1, then the line above is copied into the final external file for customer 1.
The following code is the one I'm using. However, this throws an error "Global symbol "%tempLines" requires explicit package name" for the line marked "HERE", though I'm not using a hash, and also of syntax errors in the next couple of lines w.r.t. the curly braces.
Any guidance as to the reason behind these errors would be highly appreciated.
use strict;
use warnings;
use File::Slurp;
use Data::Dumper;
my $numCustomers;
my $intHeaderFile = "internal.h";
open(my $fh, "<", "TEST.csv") or die "Unable to open CSV, $!";
open(my $infh, "<", $intHeaderFile) or die "Cannot open $intHeaderFile, $!";
my #headerLines = read_file($intHeaderFile);
chomp( my $header = <$fh> );
my #names = split ",", $header;
$numCustomers = scalar(#names) - 2;
print "\nNumber of customers : $numCustomers\n";
my #customerNames;
for(my $i = 0; $i < $numCustomers; $i++)
{
push #customerNames, $names[$i + 2];
}
my #tempHandles;
my #handles;
my #tempfiles;
my #files;
for(my $i = 0; $i < $numCustomers; $i++)
{
my $custFile = "customer".$i."_external.h";
open my $fh, '>', $custFile or die "$custFile: $!";
push #handles, $fh;
push #files, $custFile;
my $tempFile = "customer".$i."_temp.h";
open my $fh1, '+>', $tempFile or die "$tempFile: $!";
push #tempHandles, $fh1;
push #tempfiles, $tempFile;
}
while (<$fh>)
{
chomp;
my $nonIncLine = $_;
my #fields = split ",", $nonIncLine;
next if $. == 1;
for(my $i = 0; $i < $numCustomers; $i++)
{
print { $tempHandles[$i] } $fields[1], "\n" if 'Y' eq $fields[ $i + 2 ];
}
}
for(my $i = 0; $i < $numCustomers; $i++)
{
my #tempLines = read_file($tempfiles[$i]);
print #tempLines;
foreach my $headerLine(#headerLines)
{
if (grep { $headerLine =~ /$_/} #tempLines ) #HERE
{
print { $handles[$i] } $headerLine, "\n";
}
}
unlink($tempfiles[$i]);
}

perl: passing subroutines rexexp replace with search results

i have the following perl subroutine:
sub rep {
defined ($filein = shift) || die ("no filein");
defined ($fileout = shift) || die ("no fileout");
$look = shift;
$replace = shift;
open (infile, "$filein")|| die;
open (outfile, "> $fileout")|| die;
while (<infile>) {
s/$look/$replace/g;
print outfile;
}
(close the files)
}
and the following text:
kuku(fred) foo(3)
kuku(barney) foo(198)
i want to call it with the following structures:
$look = kuku\((\w+)\) foo \((\d+)\),
$replace = gaga\(($1)\) bar\(($2)\).
but when i called the sub with the following (and it's variations), i couldn't make it accept the $1, $2 format:
&rep ($ARGV[0], $ARGV[1],
"kuku\\(\(\\w+\)\\) foo \\(\(\\d+\)\\)" ,
"gaga\\(\(\$1\)\\) bar\\(\(\$2\)\\)");
all i get is:
gaga($1) bar($2)
gaga($1) bar($2)
what am i doing wrong?
how can i make the subroutine identify the $1\ $2 (...) as the search results of the search and replace?
I'm not sure if substitution part in regex can be set in a way you want it without using eval /e, so this is how I would write this.
qr// parameter is real regex, followed by callback in which $_[0] is $1
rep( $ARGV[0], $ARGV[1], qr/kuku\((\w+)\) foo \((\d+)\)/, sub { "gaga($_[0]) bar($_[1])" } );
sub rep {
my ($filein, $fileout, $look, $replace) = #_;
defined $filein or die "no filein";
defined $fileout or die "no fileout";
open (my $infile, "<", $filein) or die $!;
open (my $outfile, ">", $fileout) or die $!;
while (<$infile>) {
s/$look/$replace->($1,$2)/ge;
print $outfile;
}
# (close the files)
}
This could be even more simplified by just passing callback which would change $_.
rep( $ARGV[0], $ARGV[1], sub { s|kuku\((\w+)\) foo \((\d+)\)|gaga($1) bar($2)| } );
sub rep {
my ($filein, $fileout, $replace) = #_;
defined $filein or die "no filein";
defined $fileout or die "no fileout";
open (my $infile, "<", $filein) or die $!;
open (my $outfile, ">", $fileout) or die $!;
while (<$infile>) {
$replace->();
print $outfile;
}
# (close the files)
}

Pull regular expressions from file and compare to each line in a file

I found something that I could use on perlmonks.org (http://www.perlmonks.org/?node_id=870806) but I can't get it to work.
I can read the file without issue and build an array. Then, I'd like to compare each index of the array (each regex) to each line of a file, printing out the line before and the line after the matched line.
My code:
# List of regex's. If this file doesn't exist, we can't continue
open ( $fh, "<", $DEF_FILE ) || die ("Can't open regex file: $DEF_FILE");
while (<$fh>) {
chomp;
push (#bad_strings, $_);
}
close $fh || die "Cannot close regex file: $DEF_FILE: $!";
$file = '/tmp/mydirectory/myfile.txt';
eval { open ( $fh, "<", $file ); };
if ($#) {
# If there was an error opening the file, just move on
print "Error opening file: $file.\n";
} else {
# If no error, process the file
foreach $bad_string (#bad_strings) {
$this_line = "";
$do_next = 0;
seek($fh, 0, 0); # move pointer to 0 each time through
while(<$fh>) {
$last_line = $this_line;
$this_line = $_;
my $rege = eval "sub{ \$_[0] =~ $bad_string }"; # Real-time regex
if ($rege->( $this_line )) { # Line 82
print $last_line unless $do_next;
print $this_line;
$do_next = 1;
} else {
print $this_line if $do_next;
$last_line = "";
$do_next = 0;
}
}
}
} # End "if error opening file" check
This was working before when I had just a string per line in the file and performed a simple test such as if ($this_line =~ /$string_to_search_for/i ) but when I switched to regex in the file and a "real-time" eval statement, I now get Can't use string ("") as a subroutine ref while "strict refs" in use at scrub_file.pl line 82 and line 82 is if ($rege->($this_line)) {.
Prior to that error message, I'm receiving: Use of uninitialized value in subroutine entry at scrub_hhsysdump_file.pl line 82, <$fh> I have some understanding of that error message but can't seem to make the perl engine happy with my code thus far.
Still new to perl and always looking for pointers. Thanks in advance.
I fail to see the reason for those eval statements - all they seem to do is make the code a lot more complicated and difficult to debug.
But $rege is undef because eval "sub{ \$_[0] =~ $bad_string }" isn't working, due to the string having a syntax error. I don't know what's in $DEF_FILE, but unless it has properly-delimited regular expressions then you need to add the delimiters in the eval string.
my $rege = eval "sub{ \$_[0] =~ /$bad_string/ }"
may work, but you may need /\Q$bad_string/ instead if the strings in $DEF_FILE contain regex metacharacters and you want them to be treated as literal characters.
I suggest this version of your program which seems to do what you need without the fuss of the eval calls.
use strict;
use warnings;
use Fcntl ':seek';
my $DEF_FILE = 'myfile';
my #bad_strings = do {
open my $fh, '<', $DEF_FILE or die qq(Can't open regex file "$DEF_FILE": $!);
<$fh>;
};
chomp #bad_strings;
my $file = '/tmp/mydirectory/myfile.txt';
open my $fh, '<', $file or die qq(Unable to open "$file" for input: $!);
for my $bad_string (#bad_strings) {
my $regex = qr/$bad_string/;
my ($last_line, $this_line, $do_next) = ('', '', 0);
seek $fh, 0, SEEK_SET;
while (<$fh>) {
($last_line, $this_line) = ($this_line, $_);
if ($this_line =~ $regex) {
print $last_line unless $do_next;
print $this_line;
$do_next = 1;
}
else {
print $this_line if $do_next;
$do_next = 0;
}
}
}

Finding the last "}" of a subroutine

Supposed I have a file with Perl-code: does somebody know, if there is a module which could find the closing "}" of a certain subroutine in that file.
For example:
#!/usr/bin/env perl
use warnings;
use 5.012;
routine_one( '{°^°}' );
routine_two();
sub routine_one {
my $arg = shift;
if ( $arg =~ /}\z/ ) {
say "Hello my }";
}
}
sub routine_two {
say '...' for 0 .. 10
}
The module should be able to remove the whole routine_one or it should can tell me the line-number of the closing "}" from that routine.
You want to use PPI if you are going to be parsing Perl code.
#!/usr/bin/env perl
use warnings;
use 5.012;
use PPI;
my $file = 'Example.pm';
my $doc = PPI::Document->new( $file );
$doc->prune( 'PPI::Token::Pod' );
$doc->prune( 'PPI::Token::Comment' );
my $subs = $doc->find( sub { $_[1]->isa('PPI::Statement::Sub') and $_[1]->name eq 'layout' } );
die if #$subs != 1;
my $new = PPI::Document->new( \qq(sub layout {\n say "my new layout_code";\n}) );
my $subs_new = $new->find( sub { $_[1]->isa('PPI::Statement::Sub') and $_[1]->name eq 'layout' } );
$subs->[0]->block->insert_before( $subs_new->[0]->block ) or die $!;
$subs->[0]->block->remove or die $!;
# $subs->[0]->replace( $subs_new->[0] );
# The ->replace method has not yet been implemented at /usr/local/lib/perl5/site_perl/5.12.2/PPI/Element.pm line 743.
$doc->save( $file ) or die $!;
The following will work in case your subroutines don't contain any blank lines, like the one in your example:
#!/usr/bin/perl -w
use strict;
$^I = ".bkp"; # to create a backup file
{
local $/ = ""; # one paragraph constitutes one record
while (<>) {
unless (/^sub routine_one \{.+\}\s+$/s) { # 's' => '.' will also match "\n"
print;
}
}
}

Perl - Printing the next line

I am a noob Perl user trying to get my work done ASAP so I can go home on time today :)
Basically I need to print the next line of blank lines in a text file.
The following is what I have so far. It can locate blank lines perfectly fine. Now I just have to print the next line.
open (FOUT, '>>result.txt');
die "File is not available" unless (#ARGV ==1);
open (FIN, $ARGV[0]) or die "Cannot open $ARGV[0]: $!\n";
#rawData=<FIN>;
$count = 0;
foreach $LineVar (#rawData)
{
if($_ = ~/^\s*$/)
{
print "blank line \n";
#I need something HERE!!
}
print "$count \n";
$count++;
}
close (FOUT);
close (FIN);
Thanks a bunch :)
open (FOUT, '>>result.txt');
die "File is not available" unless (#ARGV ==1);
open (FIN, $ARGV[0]) or die "Cannot open $ARGV[0]: $!\n";
$count = 0;
while(<FIN>)
{
if($_ = ~/^\s*$/)
{
print "blank line \n";
count++;
<FIN>;
print $_;
}
print "$count \n";
$count++;
}
close (FOUT);
close (FIN);
not reading the entire file into #rawData saves memory, especially in the case of large files...
<FIN> as a command reads the next line into $_
print ; by itself is a synonym for print $_; (although I went for the more explicit variant this time...
Elaborating on Ron Savage's solution:
foreach $LineVar (#rawData)
{
if ( $lastLineWasBlank )
{
print $LineVar;
$lastLineWasBlank = 0;
}
if($LineVar =~ /^\s*$/)
{
print "blank line \n";
#I need something HERE!!
$lastLineWasBlank = 1;
}
print "$count \n";
$count++;
}
I'd go like this but there's probably other ways to do it:
for ( my $i = 0 ; $i < #rawData ; $i++ ){
if ( $rawData[$i] =~ /^\s*$/ ){
print $rawData[$i + 1] ; ## plus check this is not null
}
}
J.
sh> perl -ne 'if ($b) { print }; if ($b = !/\S/) { ++$c }; END { print $c,"\n" }'
Add input filename(s) to your liking.
Add a variable like $lastLineWasBlank, and set it at the end of each loop.
if ( $lastLineWasBlank )
{
print "blank line\n" . $LineVar;
}
something like that. :-)