How can I process a multi line string one line at a time in perl with use strict in place?
Asked Answered
B

7

8

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!

Behling answered 18/9, 2009 at 15:53 Comment(2)
If you do want to talk about best practices, I suggest you take a look at all your variable initialization, at the way you define variables like in oldfashioned C (at the top), and your use of backticks for something that can easily be achieved by Perl itself.Boater
Thanks, Manni. The directory content retrieval was just for demonstration purposes. My actual program actually calls another program and processes its output. And the old fashioned variable initialization is something I need to work more on. But I often cut & paste from old scripts and this is what I get. :-) I'm trying to do better, though.Behling
G
11

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.

Granddad answered 18/9, 2009 at 16:3 Comment(3)
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!Behling
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.Behling
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.Granddad
J
7

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.

Jessamine answered 18/9, 2009 at 16:15 Comment(0)
C
4

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.

Craniotomy answered 18/9, 2009 at 17:0 Comment(0)
E
3

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
    }
}
Enabling answered 18/9, 2009 at 15:57 Comment(1)
if you use '\n' as line separator, it will not assign \n characters to $resultLine variable. better use split /^/m, $result , it will assign whole line with line ending. Be patient, last line not allways have got end of line characters.Cornelison
C
2

Change

my $ResultsHandle = "";

to

my $ResultsHandle;
Collotype answered 18/9, 2009 at 16:2 Comment(0)
F
0

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

E.g.

open my $FOO, "dir|" or die "Can not run 'dir': $!";
Friedcake answered 18/9, 2009 at 16:0 Comment(1)
Indeed. If "dir" is a must, go for the pipe. But I'd rather use readdir or a simple glob.Boater
C
0

Better result with split can be done by:

my $result="LINE1
line2
linE3
";
#attention, /^/m allows properly operate on multiline string using regex
#and ^ is character empty begin all lines
foreach my $resultLine (split /^/m, $result) {
    print $resultline;  #withount '\n' because it have got
    #some checks & operations
}
Cornelison answered 8/12, 2016 at 9:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.