Kurt W. Leucht
Kurt W. Leucht

Reputation: 4735

How can I avoid Perl::Critic warnings when I process a multi-line string with a filehandle?

Does anyone have a solution to the task of processing a multi-line string one line at a time, other than the string-as-a-filehandle solution shown below?

my $multiline_string = "line one\nline two\nline three\nline four";
my $filehandle;
open( $filehandle, '<', \$multiline_string )
    or croak("Can't open multi-line string as a filehandle: $!");
while ( defined (my $single_line = <$filehandle>) ) {
    # do some processing of $single_line here ...
}
close( $filehandle );

My reason for not wanting to use a filehandle is pretty weak. Test::Perl::Critic whines when I have more than 10 source lines between my open command and my close command on any filehandle. I'm doing quite a bit of processing of $single_line so I actually have about 40 lines of code between my open call and my close call and I don't see any way to bring that down to 10.

And I don't really want to ignore the Perl::Critic test in my build because that's actually a decent test that I'd like to pass whenever I'm opening an actual disk file in my code.

Upvotes: 2

Views: 738

Answers (8)

brian d foy
brian d foy

Reputation: 132812

Perl::Critic is nice, but when you start obsessing about some of its arbitary requirements, it starts to waste your time rather than save it. I just let the filehandle go out of scope and don't worry about the close:

 my $multiline_string = "line one\nline two\nline three\nline four";

 {
     open my( $fh ), '<', \$multiline_string )
         or croak("Can't open multi-line string as a filehandle: $!");
     while ( defined (my $single_line = <$fh>) ) {
         # do some processing of $single_line here ...
     }
 }

A lot of people reach for regexes or split, but I think that's sloppy. You don't need to create a new list and use up a lot more memory in your program.

Upvotes: 2

Brad Gilbert
Brad Gilbert

Reputation: 34120

You could use a regex.

#!/usr/bin/perl

use strict;
use warnings;

my $s = "line one\nline two\nline three\nline four";

while ($s =~ m'^(.*)$'gm) {
    print "'$1'\n";
}

die "Exited loop too early\n" unless pos $s == length $s;

Or you could use split:

for my $line ( split m'\n', $multiline_string ){

  # ...

}

Upvotes: 0

ysth
ysth

Reputation: 98398

Um, isn't the purpose of the whine to get you to have smaller blocks of code that do just one thing? make a subroutine that does what's needed for each line.

Many people have suggested split /\n/. split /^/ is more like the filehandle way.

Upvotes: 5

Jonathan Feinberg
Jonathan Feinberg

Reputation: 45324

Make the Perl Critic happy, and make yourself even happier, by creating a subroutine, and calling it with each line of the file.

use strict; use warnings;

sub do_something {
    my ($line) = @_;
    # do something with $line
}

open my $fh, '<', \$multiline_string
    or die "Cannot open scalar for reading: $!";

while(<$fh>) {
    chomp;
    do_something($_);
}

close $fh; 

Upvotes: 10

dlamblin
dlamblin

Reputation: 45331

Personally I like using $/ to separate the lines in a multiline string.

my $multiline_string = "line one\nline two\nline three\nline four";
foreach (split($/, $mutliline_string)) {
  process_file($_);
}
sub process_file {
  my $filename = shift;
  my $filehandle;
  open( $filehandle, '<', $filename )
      or croak("Can't open multi-line string as a filehandle: $!");
  while ( defined (my $single_line = <$filehandle>) ) {
      process_line($single_line);
  }
  close( $filehandle );
}
sub process_line {
  my $line = shift;
  ...
}

Upvotes: -1

mob
mob

Reputation: 118605

Long before I even knew you could shoehorn a multiline string into a filehandle, there was split:

foreach my $single_line (split /\n/, $multiline_string) {
    # process $single_line here
    # although note that it doesn't end in a newline anymore
}

Insert disclaimer about using literal and non-portable \n here.

Upvotes: 3

Salgar
Salgar

Reputation: 7775

I might be missing something, but could you do:

my @lines = split(/\n/,$multiline_string);
foreach my $single_line (@lines) {
  ...
}

Upvotes: 3

Tom Jefferys
Tom Jefferys

Reputation: 13310

What about:

my $multiline_string = "line one\nline two\nline three\nline four";
my @lines = split(/\n/,$multiline_string);
foreach my $line (@lines) {
    #do stuff with string
}

Upvotes: 3

Related Questions