Peter’s blog ✴ Week 287 ✴ 16 September 2024
THE WEEKLY CHALLENGE
Strong and valid
You are given a string, $str.
Write a script to find if it is a valid number.
Conditions for a valid number:
A decimal number is defined as an optional sign '-' or '+' followed by one of the following:
An exponent is defined as 'e' or 'E' followed by an integer number.
Example 1 Input: $str = "1" Output: true Example 2 Input: $str = "a" Output: false Example 3 Input: $str = "." Output: false Example 4 Input: $str = "1.2e4.2" Output: false Example 5 Input: $str = "-1." Output: true Example 6 Input: $str = "+1E-8" Output: true Example 7 Input: $str = ".44" Output: true
No doubt someone will come up with one regular expression that does all of that, but I'm not that clever.
I have a main regular expression that more or less does it, but is too generous in two ways:
Each of these is then dealt with in separate regexes.
Dealing the task in multiple stages is the coolest approach and easy to follow. DIY tool on top is bonus, you would definitely love to play.
#!/usr/bin/perl # Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge use v5.26; # The Weekly Challenge - 2024-09-16 use utf8; # Week 287 - task 2 - Valid number use warnings; # Peter Campbell Smith binmode STDOUT, ':utf8'; my @tests; say qq[\nValid numbers:\n \$str Valid?\n ----- ------]; @tests = ('23', '-23', '+23', '34.1', '-34.1', '+34.1', '.7', '-.7', '23e0', '23e-1', '23E+2', '-23e10', '+34.5e-10'); valid_number($_) for @tests; say qq[\nInvalid numbers:\n \$str Valid?\n ----- ------]; @tests = ('two', '23-', '23.1.2', 'e', 'e1', '34e1.2', '34e++6', '.e5', '+.e5', '123E', '.', '.e6' ); valid_number($_) for @tests; sub valid_number { my ($str, $str2, $valid, $a, $b); $str = $_[0]; $valid = 0; # basic match $a------------ $b----- if (($a, $b) = $str =~ m|^([-+]?\d*\.?\d*)([Ee]?.*)$|) { # but if [Ee] is present it must be followed by optional sign and 1+ digits $valid = 1 if ($b eq '' or ($a ne '' and $b =~ m|^[Ee][-+]?\d+$|)); } # decimal point must be preceded or followed by a digit (or both) if ($valid and $str =~ m|\.|) { $valid = 0 unless ($str =~ m|\d\.| or $str =~ m|\.\d|); } printf("%10s %10s\n", $str, ('false', 'true')[$valid]); }
9 lines of code
Valid numbers:
$str Valid?
----- ------
23 true
-23 true
+23 true
34.1 true
-34.1 true
+34.1 true
.7 true
-.7 true
23e0 true
23e-1 true
23E+2 true
-23e10 true
+34.5e-10 true
Invalid numbers:
$str Valid?
----- ------
two false
23- false
23.1.2 false
e false
e1 false
34e1.2 false
34e++6 false
.e5 false
+.e5 false
123E false
. false
.e6 false
Any content of this website which has been created by Peter Campbell Smith is in the public domain