Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked

Saving data to file

by viored (Novice)
on Apr 08, 2014 at 04:03 UTC ( #1081458=perlquestion: print w/replies, xml ) Need Help??
viored has asked for the wisdom of the Perl Monks concerning the following question:

So I'm trying to save an arrays contents to file. I've written a short subroutine to write a line to the file, but I'm having trouble accessing all the elements of the array. Here's what I want to do
#!/usr/bin/perl #output use warnings; use strict; sub output; $main::data = [1,0,1,2,3,4,5,6,7,8,9,11,2,2,3,34,5,6,7,8,81]; $main::size = scalar @{\@{$main::data}}; for ($main::i=0; $main::i < $main::size; $main::i++) { &output($main::data[$main::i]); } sub output { open my $fh, '>>', "output.txt"; print $fh "$_[0]"; print $fh "\n"; close $fh; }
but $main::data[0] (or any other number) isn't referencing what I want it to. How can I print all the contents of the array to an output file?

Replies are listed 'Best First'.
Re: Saving data to file
by davido (Archbishop) on Apr 08, 2014 at 04:57 UTC

    Update2: Related threads where the OP has been encouraged (with more benevolent patience, apparently to no effect) to spend a little time figuring out lexicals, and the hows and whys of 'strict': Arrays and Push, Array storage issue.

    My first thought was that if you're going to fall into bugs as a direct result of abusing package globals to avoid the alarms that strict sets off, even though people have warned you about the practice, you aren't going to benefit from our help, and I may as well not bother. But if you're willing to sit through a little rant that is intended to proverbially knock some sense into you, perhaps there is hope. So....

    First, my rant:

    Don't abuse package globals in an attempt to avoid learning how to use lexical variables. And if you must continue, be honest enough with yourself to discontinue using strict. Yes, I'm advocating for not using strict until you are committed to learn why it should be used. It is doing you no good the way you are using it now anyway.

    What really should happen is this: spend an hour with perlsub, my, and strict, and another hour or so with a book like Modern Perl, or Learning Perl, so that you can come to a better understanding of why you should be using lexicals, how to use them, and why strict 'vars' is a useful means of encouraging safe practices. However, right now, the way your code includes use strict; but side-steps it is like leaving your seat belt clicked all the time so that you won't be disturbed by the "ding, ding" alarm, and then sitting on top of it because it's too hard to slide under it when it's already fastened.

    Now for the problem you are asking about:

    Your bug is on line 12; this line: &output($main::data[$main::i]);

    The reason that's a bug is because $main::data holds an array reference, but you are acting as though @main::data holds an array, which it doesn't. @main::data doesn't even exist. The simplest fix involving minimal keystrokes is to change that line to this: output($main::data->[$main::i]); This error would have been caught at compile-time if you hadn't been using fully qualified package globals in order to circumvent strictures! Had @data been a lexical, strict 'vars' would have complained about using a variable that wasn't pre-declared. How ironic that your insistence on avoiding learning how to use lexicals so that you might benefit from use strict 'vars'; has now cost you more time than it would have taken to learn to use them.

    Even the use warnings; is trying to help, by telling you that the variable you're referring to in line 12 contains an undefined value, which is a red flag.

    There's another problem, but it doesn't rise to the level of bug: scalar @{\@{$main::data}} is outright silly. Why not scalar @{\@{\@{\@{$main::data}}}}? What you are doing is taking an array ref ($main::data), dereferencing it with "@{...}", taking a reference to that with \, and then dereferencing that reference with scalar @{...}. That's an onion that needs to be peeled: scalar @{$main::data}.

    Then there's another problem, which again doesn't rise to level of bug: Inside your for loop, you are opening and closing the same file repeatedly. Why? Open it outside of the loop, write to it inside the loop, and close it when the loop finishes. If you know you're going to be writing to it repeatedly, and not reading from it in the meantime, save the overhead and complexity of opening it repeatedly.

    Here is how I would write that code:

    use strict; use warnings; my @data = qw( 1 0 1 2 3 4 5 6 7 8 9 11 2 2 3 34 5 6 7 8 81 ); open my $fh, '>', 'output.txt' or die $!; print $fh "$_\n" foreach @data; close $fh or die $!;

    Update: Other trivia which I will mention only briefly, because they are not directly causing problems, but are also either risky behavior (not checking return values of system calls), or non-idiomatic: This isn't C; you don't need to pre-declare your subroutines. This isn't Perl4; Drop the ampersand on your subroutine calls -- it will get you into trouble someday because it changes the calling semantics in a subtle way that you shouldn't have to deal with unless you really need that behavior. Make sure that your system calls are guarded by checking their return values for errors; open and close can fail, and should not fail silently. This isn't C; use range-based foreach loops to your advantage. You almost never need for( $i; $i != $n; ++$i ) {... in Perl (but very occasionally it is useful enough to be included in the language).


Re: Saving data to file
by AnomalousMonk (Canon) on Apr 08, 2014 at 04:29 UTC

    $main::data[0] accesses an element of the package global array  @main::data but there is no such array.

    There is a package global scalar  $main::data that has been initialized with a reference to an anonymous array. An element of this anonymous array can be accessed by an expression like  $main::data->[0] or (shudder)  $main::data->[$main::i]

    Had these variables been lexicals, Perl would have told you of the absence of  @data given that you are enabling warnings and strictures. As all your variables are fully-qualified package global variables, no such hints can be given.

    Update: It's a small point in the wider context of what you are doing in the OPed code, but I'm curious why the single lexical you have chosen to use in your code is in the  output() subroutine: the  $fh lexical file handle. It is very common still to see code written using package global file handles. Why, I wonder, did you choose a lexical in this instance? (Although package file handles are still very common, they are not IMHO very kosher.)

Re: Saving data to file
by 2teez (Priest) on Apr 08, 2014 at 04:32 UTC

    Hi viored,

    but $main::data[0] (or any other number) isn't referencing what I want it to. How can I print all the contents of the array to an output file?

    First off, I need ask why '$main::' i.e package variable?. I think you really need understand how these things works.
    Secondly, it's alot better to use a perl for loop, instead of the "C" type like you are using.
    Moreso, you can pass the whole array reference at ONCE into the your output subroutine, instead of one at a time.
    Some thing like this:

    use warnings; use strict; sub output; my $data = [ 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 2, 2, 3, 34, 5, 6, 7 +, 8, 81 ]; output($data); sub output { open my $fh, '>>', "output.txt" or die $!; print $fh $_, $/ for @{ shift(@_) }; }

    If you tell me, I'll forget.
    If you show me, I'll remember.
    if you involve me, I'll understand.
    --- Author unknown to me
Re: Saving data to file
by soonix (Monsignor) on Apr 08, 2014 at 05:13 UTC
    Why not
    my @data = (1,0,1,2,3,4,5,6,7,8,9,11,2,2,3,34,5,6,7,8,81);
    and then
    for (0 ... $#data) { output($data[$_]);
    or even
    for $element (@data) { output($element); }
    Update: Sorry for posting untested code - of course, that should read for my $element ...
    my bad (pun not intended, but appropriate)
Re: Saving data to file
by codiac (Beadle) on Apr 08, 2014 at 11:49 UTC
    map might be an easier way to do this.
    my $data = [1,0,1,2,3,4,5,6,7,8,9,11,2,2,3,34,5,6,7,8,81]; output($data); sub output { open my $fh, '>>', "output.txt"; print $fh map {"$_\n"} @{$_}; close $fh; }
    If you use qw, then you need to change the call to output.
    my @data = qw( 1 0 1 2 3 4 5 6 7 8 9 11 2 2 3 34 5 6 7 8 81 ); output(\@data);

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1081458]
Approved by Old_Gray_Bear
[erix]: spies should use a type-writer, no power needed, no auditable
[erix]: if all else fails, eat the paper
[erix]: a few years ago there was suddenly the news that the Kremlin was using typewriters again. Heard nothing about it afterward
[erix]: (maybe that means it worked)
[oiskuu]: Yeah, it might work as long as there are no root exploits. ;-)
[Corion]: Also, it's much harder to leak paper sheets than it is to leak documents that are available electronically
[oiskuu]: tye, do you make use of remote logging?

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (7)
As of 2017-06-23 20:22 GMT
Find Nodes?
    Voting Booth?
    How many monitors do you use while coding?

    Results (555 votes). Check out past polls.