Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:
print IsNumber("0777 891 777") . "\n"; # 0 print IsNumber("1.5671") . "\n"; # 1 print IsNumber("121A3D") . "\n"; # 0 print IsNumber("777") . "\n"; # 1 print IsNumber("0") . "\n"; # 1 print IsNumber("-4.567") . "\n"; # 1 print IsNumber("+9.8.97") . "\n"; # 0 print IsNumber("+9.897") . "\n"; # 1 print IsNumber("+9.8¬97") . "\n"; # 0 print IsNumber("9.8[97") . "\n"; # 0 sub IsNumber { my ($string) = @_; my $valid = 0; my $count = $string =~ tr/\.//; if ( $string =~ m/[a-zA-Z\ \[\]]/ ) { $valid = 0; } elsif ( $string =~ /[^\x00-\x7F]/ ) { $valid = 0; } elsif ( $count > 1 ) { $valid = 0; } elsif ( $string =~ m/[#@':;><,.{}[]=!"£$%^&*()]/ ) { $valid = 0; } elsif ( $string =~ m/^[+-]?\d+$/ ) { $valid = 1; } elsif ( $string =~ m/^[+-]?[0-9]+[.]?[0-9]+/ ) { $valid = 1; } return $valid; }
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Is A Number
by hippo (Archbishop) on Dec 17, 2021 at 15:24 UTC | |
If you are just looking for an elegant, ready-made solution then Scalar::Util::looks_like_number fits the bill:
🦛 | [reply] [d/l] |
by LanX (Saint) on Dec 17, 2021 at 15:41 UTC | |
Even better ... it's core. So no need to install anything.
Cheers Rolf | [reply] [d/l] |
|
Re: Is A Number
by haukex (Archbishop) on Dec 17, 2021 at 20:27 UTC | |
hippo already pointed you to Scalar::Util's looks_like_number, which provides access to Perl's internal string-to-number conversion. kcott pointed out some of the potential pitfalls of that function, and just to add one more: for reasons, the string "0 but true" will also cause looks_like_number to return true, and it allows leading and trailing whitespace. Another thing to consider is for example whether you want to allow leading zeros, for example, some users may expect "077" to represent the integer 63 in octal, while others might simply expect this to be the integer 77 (the latter probably being more common among non-coders). I just wanted to add that this is of course also a FAQ: How do I determine whether a scalar is a number/whole/integer/float? and that Regexp::Common::number provides more control over what strings you want to allow as numbers. | [reply] [d/l] [select] |
|
Re: Is A Number
by kcott (Archbishop) on Dec 17, 2021 at 20:02 UTC | |
The suggested Scalar::Util::looks_like_number() is often a good choice; however, be aware of some gotchas: Infinity (see "perldata: Special floating point: infinity (Inf) and not-a-number (NaN)"):
Non-decimal numbers:
"I would like to combine the string comparisons into as few as possible but seem to break the code every time I try." If looks_like_number() is causing you problems, perhaps due to "gotchas" indicated above, or as a purely academic exercise, here's some hints for a hand-crafted solution. Get rid of the blacklist: this will take ages to get right and, even then, there's a high chance that you'll miss an edge-case or two (or more). Just use a whitelist which represents valid numbers for your application. Use a single regex with alternations, and only define it once (not in every iteration). With Perl v5.10 or higher, you can use this format:
For Perls of an older vintage:
Don't try to write your regex in the smallest possible space. This isn't a golfing exercise. Readability and maintainability will very quickly deteriorate. Use the /x modifier — or /xx if available; requires Perl v5.26 or later — as explained in "perlre: /x and /xx". Consider whether you're allowing exponents:
What about when underscores have been used to improve the readability of large numbers:
Are you only dealing with 7-bit ASCII digits (/[0-9]/) or allowing the handling of all (i.e. Unicode®) digits (/\p{Digit}/)? Note that /\d/ and /[[:digit:]]/ are not necessarily the same as /[0-9]/. See "perlre: /a (and /aa)". More general sources of reference are: perlre; perlrebackslash; and, perlrecharclass. — Ken | [reply] [d/l] [select] |
|
Re: Is A Number
by syphilis (Archbishop) on Dec 18, 2021 at 01:26 UTC | |
I had a little play and noticed that your IsNumber() sub returns true for integers with embedded (and/or trailing) garbage, unless the garbage is alphabetic or whitespace. Seemed odd ... but, of course, FAIK it's quite possible that the integer inputs you're receiving are either guaranteed to have no such garbage or are to be deemed acceptable if they include such garbage. For the input array in the following script (where I also compare the IsNumber() and looks_like_number() results), IsNumber returns "1" for all but the final input. Outputs: Cheers, Rob PS I also did not expect that IsNumber('1.123e4') would return 0. But again, I don't know much about the possible inputs or the criteria for assessing them. | [reply] [d/l] [select] |
|
Re: Is A Number (updated)
by AnomalousMonk (Archbishop) on Dec 19, 2021 at 06:06 UTC | |
I agree with others that considerably simplified versions of your function are available and IMHO preferable.
I want to comment on one test in your original post. The The premature end of the character class makes the regex nonsense, but most egregiously it produces a ^ start-of-string anchor metacharacter that requires characters before it (i.e., before the start of the string) for a match to occur. Thus, no match can ever occur. Beyond that, there are two interpolated global special variables that appear in the originally posted regex and cause it to be other than what one might expect. See perlvar for $%. @' is the unused array slot of the $' regex special variable typeglob (see also perlvar).
Note that @' and $% do not appear in the dd dump of the regex – and where does the 0 come from? Fixing the character class termination and suppressing variable interpolation yields the results I think you want: Note that the @' and $% literal sequences are in their proper places in the dd dump of the regex. Same results running under Strawberry Perl 5.30.3.1 except the Possible unintended interpolation of @' in string ... warning is not emitted.
Update: If you really need to do something like Also same results under Strawberry Perl 5.30.3.1. Give a man a fish: <%-{-{-{-< | [reply] [d/l] [select] |
|
Re: Is A Number
by BillKSmith (Monsignor) on Dec 18, 2021 at 23:25 UTC | |
OUTPUT:
Bill
| [reply] [d/l] [select] |