1

Following my other question here..

I have the following piece of code where I read the contents of a webpage line by line, trying to match a certain pattern and if matched, write it to a file:

foreach my $line (split qr/\R/, $mech->content) {
    if ($line=~ m/t\/([A-Z]+)/){
        print $fileHandle "$1\n";
    }
}

I wonder whether it possible to append the matching lines to another multi-line variable and write it to a file only when the loop is finished.

The reason I want it to be that way is because I want to use the following subroutine to save data to a file, rather than doing it directly:

writeToFile("fileName.tmp","path",data);

This is a subroutine I wrote, which apart of just saving the data in a file, also checks the following:

  1. Whether the specified path already exists (and creates it if needed)
  2. If the file already exist on disk, compare it to the one which is about to be written (and write it only in case the files are different)

So if I'll be able to create this additional variable, I will have one less file to write to a disk.

Community
  • 1
  • 1
Eugene S
  • 6,709
  • 8
  • 57
  • 91
  • I will remove [tag:algorithm] from it. I guess there won't be any magic in this (yet I know nothing about [tag:perl]) – Alexander Mar 28 '13 at 20:05

3 Answers3

4
  1. You can do it by string concatenation, inside your loop you simply replace print with

    $data .= "$1\n"; 
    # Same as $data = $data . "$1\n";
    
  2. Or use the magic of Perl's map to build the multiline string, without having either a loop or an if:

    my $data = join "", 
                    map { /t\/([A-Z]+)/ ? "$1\n" : "" } 
                    split(qr/\R/, $mech->content);
    
  3. Or, you don't even have to go through the pain of splitting and joining.

    Simply treat your multiline string as a single string (/g modifier lets the regex match again and again), and replace your entire loop with just 3 lines:

    my $data = $mech->content();
    $data =~ s/t\/([A-Z]+)/$1\n/g;
    writeToFile("fileName.tmp", "path", $data);
    
DVK
  • 126,886
  • 32
  • 213
  • 327
  • Thanks a lot for your detailed answer. Could you please explain how the output of the expression (number 3) can be stored to a variable / written do a file? – Eugene S Mar 28 '13 at 20:29
  • @EugeneS - see my update. "Output" of expression 3 is just the original variable itself - the regex substitutes "in-place" in Perl. – DVK Mar 28 '13 at 20:32
  • And #3, ladies and gentlemen, is why Perl is Good. – DVK Mar 29 '13 at 06:00
1

It's simple - you can append characters by using .= operator (which is comboned assignment (=) and concatenation (.):

my $data = "";
while (my $line = ...) {
    ...
    $data .= $line . "\n";
}
writeToFile("fileName.tmp", "path", $data);
  • Thank you for your answer. Can you please explain why the last `.` is needed (after `$line`)? – Eugene S Mar 28 '13 at 20:22
  • 1
    @EugeneS - in his code, it's needed as concatenation operator. Personally, in our company I always enforce a style which uses interpolation over concatenation, e.g. `"$line\n"` is better than `$line . "\n"` in my experience, style wise – DVK Mar 28 '13 at 20:24
  • @DVK So the `.` just concatenates lines? – Eugene S Mar 28 '13 at 20:30
  • 1
    @EugeneS - . concatenates any strings. `$line . "\n"` simply concatenates the string with newline. – DVK Mar 28 '13 at 20:30
1

Just replace

open(my $fileHandle, ...) or ...;

...

   print $fileHandle "$1\n";

with

my $data = '';

...

   $data .= "$1\n";

to append the output to a string instead of sending it to a file handle.

ikegami
  • 367,544
  • 15
  • 269
  • 518