views:

1094

answers:

6

I'm trying to figure out the proper PBP approved way to process a multi line string one line at a time. Many Perl coders suggest treating the multi line string as a filehandle, which works fine unless you have "use strict" in your script. Then you get a warning from the compiler about not being able to use a string as a symbol while strict refs is in use.

Here's a simple working example of the problem:

#use strict;
use warnings; 

my $return = `dir`;
my $ResultsHandle = "";
my $matchLines = "";
my $resultLine = "";
open $ResultsHandle, '<', \$return;
while (defined ($resultLine = <$ResultsHandle>)) {
    if ($resultLine =~ m/joe/) {
        $matchLines = $matchLines . "\t" . $resultLine;
    }
}
close($ResultsHandle);
print "Original string: \n$return\n";
print "Found these matching lines: \n$matchLines\n";

Notice that the "use strict" line is commented out. When I run this script without use strict, I get what I want and expect:

Original string: 
 Volume in drive D has no label.
 Volume Serial Number is 50D3-54A6

 Directory of D:\Documents and Settings\username\My Documents\Eclipse\myTestProject

09/18/2009  11:38 AM    <DIR>          .
09/18/2009  11:38 AM    <DIR>          ..
09/18/2009  11:36 AM               394 .project
09/18/2009  11:37 AM                 0 joe.txt
09/18/2009  11:37 AM                 0 joey.txt
09/18/2009  11:38 AM                 0 kurt.txt
09/18/2009  11:43 AM               497 main.pl
09/18/2009  11:38 AM                 0 shane.txt
               6 File(s)            891 bytes
               2 Dir(s)   6,656,188,416 bytes free

Found these matching lines: 
    09/18/2009  11:37 AM                 0 joe.txt
    09/18/2009  11:37 AM                 0 joey.txt

Here's the problem, though. When I uncomment the "use strict" line, I get the following warning or error from Perl:

Can't use string ("") as a symbol ref while "strict refs" in use at D:/Documents and Settings/username/My Documents/Eclipse/myTestProject/main.pl line 8.

Line 8 is the "open $ResultsHandle, '<', \$return;" line, by the way. So since Perl Best Practices requires me to use strict, how does PBP expect me to process a multi line string one line at a time? Any suggestions from the SO community?

Thanks!

+1  A: 

Convert the multi-line string into a list of single line strings with split:

my @resultLines = split /\n/, $result;     #   or  /\r\n/ for Windows?
foreach my $resultLine (@resultLines) {
    if ($resultLine =~ m/joe/) {
        $matchLines
            = $matchLines . "\t" 
                 . $resultLine . "\n";  # put \n or \r\n back on the end
    }
}
mobrule
A: 

Open a filehandle using a pipe from "dir" command.

E.g.

open my $FOO, "dir|" or die "Can not run 'dir': $!";
DVK
Indeed. If "dir" is a must, go for the pipe. But I'd rather use readdir or a simple glob.
innaM
+2  A: 

Change

my $ResultsHandle = "";

to

my $ResultsHandle;
moonshadow
+8  A: 

Don't initialise $ResultsHandle:

use strict;
use warnings; 

my $return = `dir`;
my $ResultsHandle;  # <-- leave undefined
my $matchLines = "";
my $resultLine = "";
open $ResultsHandle, '<', \$return;
while (defined ($resultLine = <$ResultsHandle>)) {
    if ($resultLine =~ m/joe/) {
        $matchLines = $matchLines . "\t" . $resultLine;
    }
}
close($ResultsHandle);
print "Original string: \n$return\n";
print "Found these matching lines: \n$matchLines\n";

If you leave $ResultsHandle undefined before the open(), it will be filled in with a reference to the file handle. Because you were setting it to a string, open() presumed that it was supposed to be a symbolic reference to a variable instead --- not allowed under use strict.

Dave Hinton
Wow. Thanks! Shows how little I know about Perl! I guess I thought I had to initialize it to SOMETHING. Guess I was wrong. Thanks for the quick answer!
Kurt W. Leucht
Also shows that I don't understand "strict" at all. I've been just trying to please the compiler and the Perl Critic module both without fully understanding all the messages they've both been generating.
Kurt W. Leucht
Another way to think about it: `open()` initialises `$ResultsHandle` for you, *if it's not already been initialised*. `use strict` disallows things which can be useful occasionally, but more often cause trouble.
Dave Hinton
+6  A: 

The more succinct PBP way is to use open like so:

open my $ResultsHandle, '<', \$return;

This eliminates the need for that earlier "my $Resultshandle;" declaration and avoids incurring that strict warning that you ran into.

/I3az/

draegtun
+3  A: 

You can also use a regexp as an iterator:

my $data = q{Hello
This
Is
A
Test};

while( $data =~ /(.+)$/mg) {
    print "line is '$1'\n";
}

This is slightly less convoluted compared to using a filehandle that represents a string.

jrockway