Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Summing numbers in a file

by pvfki (Novice)
on May 29, 2020 at 23:33 UTC ( [id://11117483]=perlquestion: print w/replies, xml ) Need Help??

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

If I have a file with two numbers, say: numbers.txt:
2+3
I want to create a program which will output the sum of those numbers in a different file called sum: sum.txt:
5
Here is what I have so far: sum.pl
#!/usr/bin/perl -w use warnings 'all'; use strict; my $filename = 'numbers.txt'; open(N, '<', $filename); chomp (my $numbers = <N>); my $sum = eval $numbers; my $outfile = "sum.txt"; open FILE, '>'.$outfile; print FILE $sum; close FILE;
Use of uninitialized value $sum in print at sum.pl line 17, <N> line 1.

The file could actually have more than two numbers, the program just needs to read the string expression in the file, and output the sum to another file. Thanks for help.

Replies are listed 'Best First'.
Re: Summing numbers in a file
by Athanasius (Archbishop) on May 30, 2020 at 01:29 UTC

    Hello pvfki,

    When I run your code as written, it works as expected with no errors or warnings. I note that the uninitialized message refers to “line 17” which doesn’t correspond to the code you’ve shown. So I don’t know what the actual problem may be.

    A few observations:

    • In modern Perl, the preferred style is to use a scalar variable for a file handle: open(my $N, '<', $filename);
    • You should check each open and close for possible failure. The simplest way to do this is by adding use autodie; to the head of your script.
    • Prefer the 3-argument form of open for writing as well as reading: open(my $FILE, '>', $outfile);
    • Since you explictly close FILE;, you should also close N;.

    Extending the script to process multiple lines just requires a loop:

    use warnings 'all'; use strict; use autodie; my $filename = 'numbers.txt'; my $outfile = 'sum.txt'; open(my $in, '<', $filename); open(my $out, '>', $outfile); while (my $numbers = <$in>) { chomp $numbers; my $sum = eval $numbers; print $out $sum, "\n"; } close $out; close $in;

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      the preferred style is to use a scalar variable for a file handle: open(my $N, '<', $filename);

      I will agree with this in the context of a sub that opens a file and closes it before returning, or that returns a file handle. Indeed, I would argue that using a global handle in either of those cases is incorrect. However, I will quibble with this at top-level as in this case: there is no functional difference between the lexical file handles in your example and the traditional global file handles — in both cases, a handle opened at top-level is defined until the end of the script and valid until closed.

      Please correct me if I am somehow misinformed about this.

      PS: Our questioner forgot error handling, so I will point out that open ... or die is an important Perl idiom that is hidden in your example behind the autodie pragma.

        Hello jcb,

        However, I will quibble with this at top-level as in this case: there is no functional difference between the lexical file handles in your example and the traditional global file handles — in both cases, a handle opened at top-level is defined until the end of the script and valid until closed.

        Well, for this particular script, that is quite true. And actually, my advice was only meant as indicating good practice in general. However, it is easy to show that a lexical file handle may be a better choice even at the top level:

        Yes, this is a highly contrived example. But then, why take even remote risks when they can be easily eliminated by the consistent application of good practice?

        Hope that’s of interest,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        there is no functional difference between the lexical file handles in your example and the traditional global file handles

        Please see this recent discussion about lexical vs. bareword filehandles. In particular, open(N, "<", $filename); chomp (my $numbers = <M>); only gives warnings, while open(my $N, "<", $filename); chomp (my $numbers = <$M>); is of course a fatal error under strict. Also, bareword filehandles clash with package names.

Re: Summing numbers in a file
by kcott (Archbishop) on May 30, 2020 at 06:08 UTC

    G'day pvfki,

    "my $sum = eval $numbers;"

    I'd be extremely wary of doing that as is. Whether through innocent accident or intended maliciousness, using eval on an unknown string could be disasterous.

    You should include some validation of that input. Perhaps split /[+]/, $numbers then check that every element returned matches the type of number you're going to allow (integer/float; positive/negative; range; and so on).

    — Ken

Re: Summing numbers in a file
by parv (Parson) on May 30, 2020 at 07:51 UTC
Re: Summing numbers in a file
by hippo (Bishop) on Jun 03, 2020 at 12:27 UTC

    Building on everyone else's comments and leveraging Math::Calc::Parser, here is a simple yet reasonably secure solution.

    #!/usr/bin/env perl use strict; use warnings; use autodie; use feature 'say'; use Math::Calc::Parser 'calc'; open my $in, '<', 'numbers.txt'; open my $out, '>', 'sum.txt'; while (<$in>) { say $out calc $_; } close $out; close $in;
Re: Summing numbers in a file
by Tux (Canon) on Jun 03, 2020 at 10:40 UTC

    I wonder why most of this thread is talking about file handle safety and none of those even mention what will happen if a line in the file is rm -rf /. (thanks for the posts that did mention "do not use eval". eval is dangerous!)

    I would not even consider testing this script without a minimal validation of it's input. A start could be:

    use 5.16.3; use warnings; use autodie; open my $in, "<", "numbers.txt"; open my $out, ">", "sum.txt"; while (<$in>) { m{\S} or next; # skip empty lines m{^\s*#} and next; # skip comment lines m{^[-.0-9+*/ ]+$} or die "Line too dangerous to evaluate!"; # . fo +r floats # pattern needs extending for math (sqrt, log, sin, cos, ...) supp +ort and exponentials with e notation # and 'x' for hexadecimal, '(', and ')' for grouping etc etc. Anyw +ay, it is dangerous. # I'd not use eval for safety reasons, but if your validator is st +rict enough my $result = eval $_; $@ and die "Invalid expression on line $."; print $out $result; } close $in; close $out;

    Enjoy, Have FUN! H.Merijn
Re: Summing numbers in a file
by soonix (Canon) on May 30, 2020 at 09:19 UTC
    To elaborate on Athanasius's remark about "line 17": such things like to happen, if/when I have script and execution open in different windows, and forget to save my changes before trying them out. Or perhaps you edited in a different directory/folder than you executed it…
Re: Summing numbers in a file
by perlfan (Vicar) on May 31, 2020 at 06:27 UTC
    There is no question, I would use WWW::WolframAlpha or duck duck go APIs. And with Wolfram Alpha, get all kinds of other information.

      The question asked was 'If I have a file with two numbers, say: numbers.txt ... I want to create a program which will output the sum of those numbers in a different file called sum: sum.txt:'. The response that you'd use a computer more than capable of adding numbers together, to call a third party online service to achieve this is saddening.

        It’s the way of the world. A kind of micro-offshoring. :P

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11117483]
Approved by Athanasius
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (5)
As of 2024-03-28 13:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found