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

hi guys , so here's my code to make you understand my problem

open (FILE,"rs.txt") ; @res=<FILE>; print "@res";

in "res.txt" file there's a line and in this line there's "1+2" for exp...so i'm trying to pirnt the result , i mean 3 not the same things agian....hope you understand and sorry for my bad english :)

Replies are listed 'Best First'.
Re: converting from str to int
by davido (Cardinal) on Jul 01, 2014 at 20:05 UTC

    The path of least resistance as a developer is to just eval the string, letting Perl do the work. But that can be fraught with peril if the input is not completely under your control and known to be syntactically sound, and free from bad behavior. Here's an example:

    while( <DATA> ) { chomp; print "( $_ ) = ( ", eval($_), " )\n"; }

    But this is almost certainly a bad idea, since your data could contain any arbitrary Perl code -- and we all know how much trouble one can get into with arbitrary Perl code.

    So the developer seeking the path of least resistance might then think to gravitate toward the Safe module. Using that module one can cherry-pick those operations that are to be considered well-behaved and benign enough to allow for their execution; a constrained subset of arbitrary user code. And that code is executed in a sort of sand box that minimizes its ability to interact with the underlying system. Safe seems like a fairly safe approach.

    But it's really going in the wrong direction. The end goal isn't to get perl to parse and execute a safe subset of Perl. The goal is for your script to parse, comprehend, and evaluate mathematical equations. This is one of those things that every first-year CS student takes on at one point or another. A minimal implementation is discussed within the first couple of chapters in SICP. MJD works his reader through a simple implementation early-on in Higher Order Perl. There may be (though I don't remember for sure) a discussion of an implementation in Mastering Algorithms with Perl, as well. The trick is to tokenize, parse into a tree, and traverse the tree while applying the appropriate operators to the appropriate factors.

    While this isn't terribly complex, it's not really worth doing all over again unless you're trying to learn how to do it. It's been done a million times before. And as it turns out, there's at least one (and probably many) solutions on CPAN. One I noticed is authored by mortiz: Math::Expression::Evaluator. This module provides a simple interface to a set of black boxes that do all of the work for you. Here is an example:

    use Math::Expression::Evaluator; while( <DATA> ) { chomp; my $m = Math::Expression::Evaluator->new; eval { my $p = $m->parse($_); print "( $_ ) = ( ", $p->val, " )\n"; 1; } or warn "Unable to resolve expression ($_).\n"; } __DATA__ 1+2 1 + 2 2 * 2 + 4 asdf#@fg

    When you run that, you get:

    ( 1+2 ) = ( 3 ) ( 1 + 2 ) = ( 3 ) ( 2 * 2 + 4 ) = ( 8 ) Unable to resolve expression (asdf#@fg).

    No need to implement your own, no need to deal with string eval, or Safe. Done. ;)


    Dave

Re: converting from str to int
by AppleFritter (Vicar) on Jul 01, 2014 at 17:29 UTC

    You could use eval to evaluate the expression(s) read from your file...

    ...but depending on where said file comes from and to which extent you trust (or distrust) its contents, it may be a bad idea to do so. Safe might help with that, though I never used it myself.

    The Monastery's search function also immediately turned up Evaluate Expressions., BTW, which may be relevant.

Re: converting from str to int
by pvaldes (Chaplain) on Jul 01, 2014 at 17:32 UTC
    open (my $FILE, '<',"res.txt") or die $! ; while (<$FILE>){ chomp; my ($foo,$bar) = split /\+/, $_, 2; print $foo + $bar,"\n";}
Re: converting from str to int
by ww (Archbishop) on Jul 01, 2014 at 18:38 UTC

    The sketch offered by pvaldes makes a good starting point... but there are a lot of possible cases about which you've given us inadequate information.

    For rs.txt where the format is similar to any of these, one (long-winded but explicit) solution might be:

    #!/usr/bin/perl use strict; use warnings; use 5.016; my @line = ("1+2 blah blah", "leading words 1+2 blah blah", "spaces around arith op: 1 + 2 blah blah", ); for my $line(@line) { chomp $line; my ($foo,$bar) = split /\s*\+\s*/, $line; $foo =~ s/.*([0-9]+).*/$1/; $bar =~ s/.*([0-9]+).*/$1/; say $foo + $bar . " is total at Ln 16"; } __END__ output: 3 is total at Ln 16 3 is total at Ln 16 3 is total at Ln 16

    But, clearly, this is not a comprehensive list of possible data formats. Next time, please do as FAQS, such as How do I post a question effectively?, On asking for help and Writeup Formatting Tips suggest.



    Quis custodiet ipsos custodes. Juvenal, Satires

Re: converting from str to int
by AnomalousMonk (Archbishop) on Jul 01, 2014 at 22:03 UTC

    Here's another stab at a possible solution. A robust parser such as has been discussed by others would probably be better, but something like this may serve.

    c:\@Work\Perl>perl -wMstrict -le "my $operators = '+-*/'; my %disp = map { $_ => eval qq{ sub { return \$_[0] $_ \$_[1] } } } split '', $operators ; my $int = qr{ [-+]? \d+ }xms; my $op = qr{ [\Q$operators\E] }xms; ;; my $t = 'foo 1 + 2 bar 34*56 baz 3- 4 and 56 /78 too'; ;; for my $s ($t, @ARGV) { print ''; print qq{'$s'}; $s =~ s{ ($int) \s* ($op) \s* ($int) }{ $disp{$2}->($1, $3) }xmsge; print qq{'$s'}; } " "x-3--4,-3++4" "foo+bar" 'foo 1 + 2 bar 34*56 baz 3- 4 and 56 /78 too' 'foo 3 bar 1904 baz -1 and 0.717948717948718 too' 'x-3--4,-3++4' 'x1,1' 'foo+bar' 'foo+bar'
    ... code to make you understand my problem ...

    In this spirit, I was tempted to post only the code from the statement
        my $t = 'foo 1  + 2 bar 34*56 baz 3-  4 and 56  /78 too';
    on down and title it something like "code to make you understand my solution", but let's just say no more...

    Update: For instance, the code above fails for something as simple as  "1 + 2 + 3" which even a fairly simple parser could handle. But again, we don't know just what's really needed...

Re: converting from str to int
by Laurent_R (Canon) on Jul 01, 2014 at 17:47 UTC
    Of course, we would need to see the content of the the "res.txt" file to ascertain that the above proposed solutions will work on it. (Or at least you should confirm that there is nothing else that "1+2" in your file. If not, then some additional preprocessing might be needed.)
Re: converting from str to int
by choroba (Cardinal) on Jul 01, 2014 at 21:03 UTC
    Check the initial example in the documentation of Marpa::R2. What remains is to pass it the string you read from the input file.
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: converting from str to int
by DrHyde (Prior) on Jul 02, 2014 at 10:00 UTC
    You're opening the file wrong. Try this:
    open(FILE, "-|", "bc < res.txt"); @res=<FILE>; print "@res";
      bc may be a good hint, but regarding your wording, there's technically nothing wrong with the way the OP is opening the file. The only thing that might be "wrong" with it is that there's no error checking - which your example doesn't do either. e.g. open(FILE, "-|", "bc < res.txt") or die "failed to pipe: $!";
Re: converting from str to int
by perlfan (Parson) on Jul 02, 2014 at 12:19 UTC
    This just sounds like a bad idea. There are examples of arithmetic parsers out there, and I'd recommend this since you don't want to try to evaluate an invalid expression (unmatched parens, for example).

      This just sounds like a bad idea. There are examples of arithmetic parsers out there, and I'd recommend this since you don't want to try to evaluate an invalid expression (unmatched parens, for example).

      Hmm, its bad idea to do homework? I'm not sure what you're saying perlfan