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

Greetings fellow monks,

A friend of mine was bug checking a script I wrote and wondered about comparing strings to numbers, and via that conversation, I wondered about parsing strings for numbers. I've never really worried about it until now. What I've come up with seems both crude and prone to problems. Is there a better/smarter way to go about this?

#!/usr/bin/perl use warnings; use strict; my $string = '123.45abc'; print parseInt($string), "\n"; exit; sub parseInt { my $val = shift; $val++; $val--; return $val; }

Looking at JavaScript, you can functions like parseInt or parseFloat fairly safely. Any ideas for Perl?

gryphon
code('Perl') || die;

Replies are listed 'Best First'.
Re: Parse float from string
by jryan (Vicar) on Nov 05, 2002 at 01:02 UTC
    Take a look at this entry from the perlfaq.
Re: Parse float from string
by BrowserUk (Patriarch) on Nov 05, 2002 at 00:39 UTC

    I recently banged my head on exactly this problem, and tye pointed out that Perl has it own internal routine for deciding whether a scalar is a valid number.

    It seems a shame that this internal routine is not exposed to the programmer, and I made an attempt to gain access to it (under AS) using Win32::API to access the internal routine looks_like_number() function in perl56.dll directly. Unfortunately, it proved impossible (for me anyway) to gain access to the address of a scalar in a form that it would accept from within perl. Tye kindly tracked this down to being something to do with it needing a thread-safe verion of the pointer.

    I believe that it would be possible to write a small XS module to expose the function, which would make a lot more sense than everyone having to rewrite it's logic using regexs or whatever, but I am not yet ready to go delving into the excesses of XS.

    Any takers?

    Maybe they will expose it in a later version of Perl. Who knows.


    Nah! Your thinking of Simon Templar, originally played by Roger Moore and later by Ian Ogilvy
      The XS code is trivial (aside from the name of the module - I used a dummy for illustration purposes :-)):
      #include "EXTERN.h" #include "perl.h" #include "XSUB.h" MODULE = Foo::IsNum PACKAGE = Foo::IsNum int looks_like_number(sv) SV *sv
      However I don't think this would quite solve the problem.
      In particular it returns false when testing the string "123abc" (which makes sense, this isn't a number). So to extract the number I think you still need to consider using a regexp.

      email me or /msg me if you'd like to have the code to play around with...

      Michael

        Your right. It wouldn't help a great deal in this particular case.

        The problem I had trying to access it without resorting to XS came (I was reliably informed) because it (or at least the version exported by perl56.dll under AS/build633multi-threaded) requires that it is passed a pTHX_ SV* rather than a simple SV* as shown below and it isn't possible to get at one of those from within Perl itself. I could well be wrong on that, but I failed spectacularly for 3 days to achieve it.

        PERL_CALLCONV I32 Perl_looks_like_number(pTHX_ SV* sv);

        As for the XS. I am not, nor do I wish to be at this point, set up to go through the pain of building my own XS modules. I am managing to keep myself busy trying to learn the in's and out's of perl itself. A process which I am thoroughly enjoying. I don't wish (at this time) to pollute that joy by getting into the world of XS which I have seen several monks who's abilities I much respect, complain publicly (though possibly in part in jest) regarding the vaguries of using XS.

        Thanks a lot for the offer though. I wonder, but don't have enough insentive at this stage to research it, how hard it would be to create a module that would reliably work cross platform?

        Please excuse my ignorance if what you offered would do that, but I mean one that I could install with the need to compile it.


        Nah! Your thinking of Simon Templar, originally played by Roger Moore and later by Ian Ogilvy
Re: Parse float from string
by mpeppler (Vicar) on Nov 05, 2002 at 00:20 UTC
    Personally I'd use a regexp to do this. Something like:
    sub parseInt { my $str = shift; $str =~ /(\d+)/; return $1; }
    Michael

      If you're going to use a regexp to parse a number (which seems to me to be the right approach as well), the best bet would be to use Conway's Regexp::Common module (actually Regexp::Common::number but that link dumps you into a page of results) which does this for you (and one would assume that it gets all the hairy bits right).

      use Regexp::Common qw/number/; sub make_num { return $_[0] =~ /(^$RE{num}{real}$)/ ? $1 : undef; }

      tweaked: s/is_num/make_num/ and changed the return value, in order to be more in line with the original question.



      print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u'
      Unfortunately, that won't catch the period character. I would use transliteration, IF you know what characters you should ignore.

      $string =~ y/A-Za-z //d;

      That'll remove alphabeticals and spaces. However, you may have to specify more characters if there's more garbage in your $string's.

      --
      perl: code of the samurai

        Sorry if I misread - the original poster had a parseInt() sub defined...
        To parse a float:
        sub parseFloat { my $str = shift; $str =~ /([\d\.]+)/; return $1; }
        Michael
Re: Parse float from string
by Anonymous Monk on Nov 05, 2002 at 03:46 UTC
    How about $val += 0;
Re: Parse float from string
by gryphon (Abbot) on Nov 05, 2002 at 17:31 UTC

    You know, this really seems like it ought to be a really basic function in Perl. Perl is uber-powerful with text manipulation, why not add just a simple "make number" function? JavaScript does this. The "parseInt" and "parseFloat" functions are extremely useful. Maybe this belongs in meditations, but Perl's ability to think of scalars as either numbers of strings can be both useful and annoying at the same time. Maybe this is something to add into Perl 6?

    Update: OK, I'm an idiot. Nevermind.

    gryphon
    code('Perl') || die;

      The function you are talking about already exists, it is strtod() from POSIX module.
Re: Parse float from string
by webengr (Pilgrim) on Nov 05, 2002 at 15:41 UTC
    This works, but I don't know if it is what you are after:
    #!/usr/bin/perl -w use strict; my $string = "4 I am thinking99 of a 34.56number" . "be110.2tween 100 an100d 1000, " . "and it isn't 55..."; # forgot the '+' qualifier on the split token! my @ary = split /[^\d.]+/, $string; for (@ary) { # don't need this anymore # next if /^$/ or !/\d/; # because of the way we split, # there could be multiple ".." s/[.]+/\./; # convert string to number (thanks to prev suggestion) $_ += 0; print $_, "\n"; }
    It ain't pretty, but it does the trick.

    UPDATE: fixed the split token to eliminate empty strings



    PCS