Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi everyone, I have created an array and want to append it to a file without an "foreach" statement and with spaces between the array elements. I hv a good running script but I need to make it more memory efficient.Need all the help here is a sample code.
foreach $x(@x) { open ($x, ">>$log") or die ; print while (<$x>); printf "%7d",$x; } close ($x);

janitored by ybiC: Balanced <code> tags around codeblock

Replies are listed 'Best First'.
Re: efficient Array printing to a file
by Somni (Friar) on May 28, 2004 at 01:25 UTC
    foreach $x (@x) { open ($x, ">>$log") or die; print while (<$x>); printf "%7d",$x; } close ($x);

    Your code doesn't make a whole lot of sense; you appear to be using $x for more than one thing, and it's probably going to lead to problems. I'm not sure how this code can be working as you say it is. I will attempt to explain.

    You say you want to append the elements of an array, @x to your file, $log. This means @x is filled with strings, perhaps long, perhaps short. You then iterate over them (foreach $x (@x)) and open a handle (open($x, ">>$log")) based on the string in @x. This means, if @x = ('foo bar baz', 'STDIN') you just opened the filehandles foo bar baz and STDIN. The STDIN example is an important one, as it illustrates the problem with this logic; by opening it, you just closed the STDIN you already had open. This same concept applies to any filehandle you may have open in your script. Also, this method of opening a filehandle is a symbolic reference, which is disallowed under use strict. You are using strict, right?

    Once you've opened your file for appending you then go on to read from it (print while (<$x>)). You cannot read from a file opened for appending, and given what you're trying to do, I'm not sure why you're trying. I think you intended to print $x to the filehandle $x; there is a better way to do this, and I will cover it further down.

    Finally, you close $x, but you do it outside of the foreach loop. At that point $x is undefined; you should have seen a warning about attempting to close undef; you are using warnings, right?

    Now, as I mentioned, there is a much easier way to do this. You say you want to append the contents of @x to $log, and format each element with %7d:

    open(my $logfh, ">>$log") || die("Unable to open log file \"$log\": \l$!.\n"); print $logfh join(" ", map { sprintf "%7d" $_ } @x); close($logfh);

    I specifically used $logfh like that to illustrate a point; if you're opening a filehandle in a loop or other block, you don't need to use some random string (such as what's in $x).

      Hi I understand what you are saying here...but my code works too.....hmm ... but the line print $logfh join(" ", map { sprintf "%7d" $_ } @x); is erroring out
        print $logfh join(" ", map { sprintf "%7d", $_ } @x)

        Note the comma after sprintf's format and $_. It was not in my original code. Also, that does not print a newline, which may be important.

Re: efficient Array printing to a file
by biosysadmin (Deacon) on May 27, 2004 at 22:41 UTC
    First try wrapping your sample code in code tags to make it readable, so it looks like this:
    foreach $x (@x) { open ($x, ">>$log") or die; print while (<$x>); printf "%7d",$x; } close ($x);
    A more common idiom for printing to a file would be this:
    open (OUTPUT, ">>$log") or die "Error opening file $log ($!)\n"; my $line = join ' ', @x; print OUTPUT $line, "\n"; close (OUTPUT) or die "Error closing file $log ($!)\n";
    Catching possible errors from your system IO operations in $! is generally a good practice. Also, the version I showed doesn't reopen the filehandle every time you want to print a array element to the file. The join operator is very useful, do a perldoc -f join if you've never seen it before. A perldoc -f open would probably be helpful to you as well.

    Hope that this helps. :)

      Hi there!!, Thank you for a very quick reply...that indeed is a very good suggestion..and it it works fine too..I wanted to try "printf" so that I have more control over the output. :-{>
Re: efficient Array printing to a file
by Zaxo (Archbishop) on May 27, 2004 at 23:02 UTC

    print takes a list, so there's no need to loop. You can take advantage of $", the list separator,

    { local $" = ' '; open my $lgh, '>>', $log or die $!; print $lgh "@x", $/ or die $!; close $lgh or die $!; }
    You could equally well set the output record separator $, to space and leave off the doublequotes.

    After Compline,
    Zaxo

      Unless he's altered $" elsewhere (and I doubt it, because if he were familiar with $" I believe he'd have answered his own question already), there's no need to set it. It defaults to a single space.

      Also, there is no need to explicitly use $/, either. If he wants line-ending, he can just use "\n", which Does The Right Thing on non-UNIX platforms.

      --rjray