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

Dear Monks,

I found on the perlfaq4 the following code:

use 5.010; given( $number ) { when( /\D/ ) { say "\thas nondigits"; continue } when( /^\d+\z/ ) { say "\tis a whole number"; continue } when( /^-?\d+\z/ ) { say "\tis an integer"; continue } when( /^[+-]?\d+\z/ ) { say "\tis a +/- integer"; continue } when( /^-?(?:\d+\.?|\.\d)\d*\z/ ) { say "\tis a real number"; continue } when( /^[+-]?(?=\.?\d)\d*\.?\d*(?:e[+-]?\d+)?\z/i) { say "\tis a C float" } }

I am trying to find a way to determine the element from the array if it is an integer, decimal or float. But I am guessing that I am missing something.

I have modified the code for testing purposes:

#!/usr/bin/perl use strict; use warnings; my @numbers = (1, -1, 123.1, 0.1); foreach my $number (@numbers) { if ($number =~ /\D/ ) { print "The $number has nondigits\n"; } elsif ($number =~ /^\d+\z/ ) { print "The $number is a whole number\n"; } elsif ($number =~ /^-?\d+\z/ ) { print "The $number is an integer\n"; } elsif ($number =~ /^[+-]?\d+\z/ ) { print "The $number is a +/- integer\n"; } elsif ($number =~ /^-?(?:\d+\.?|\.\d)\d*\z/ ) { print "The $number is a real number\n"; } elsif ($number =~ /^[+-]?(?=\.?\d)\d*\.?\d*(?:e[+-]?\d+)?\z/i ) { print "The $number is a C float\n"; } }

But I am getting, not the expected output:

The 1 is a whole number The -1 has nondigits The 123.1 has nondigits The 0.1 has nondigits

I would expect something like:

The 1 is a +/- integer The -1 is a +/- integer The 123.1 is a C float The 0.1 is a C float

Update: I was playing around to see if by modifying the numbers to string in the array will affect the output. I modified the my @numbers = (1, 1.0, 123.1, 0.1); to my @numbers = qw(1, 1.0, 123.1, 0.1);. That was the only modification that I applied and the output of the script changes only the first line The 1 is a whole number to The 1, has nondigits.

At this point I am sure that I am doing something completely wrong but I can not figure out what is it. Can someone help understand why I can not get the expected output?

Update 2: I also found from Is it a Number?.

my @numbers = ("1", "-1", "123.1", "0.1"); foreach my $number (@numbers) { my $integer = is_integer($number); print "$number is Integer\n" if ($integer); my $float = is_float($number); print "$number is Float\n" if ($float); } sub is_integer { $_[0] =~ /^[+-]?\d+$/ } sub is_float { $_[0] =~ /^[+-]?\d+\.?\d*$/ } __END__ Output: 1 is Integer 1 is Float -1 is Integer -1 is Float 123.1 is Float 0.1 is Float

Update 3: As shmem pointed out, what I was afraid of, that regex compare against strings. So I guess the array to compare against should be modified to my @numbers = qw(1  1.0  123.1  0.1); as Laurent_R has pointed out. So the code should be like this:

#!/usr/bin/perl use strict; use warnings; my @numbers = qw(1 -1 123.1 0.1 Characters); foreach my $number (@numbers) { if ($number =~ /^[+-]?\d+\z/ ) { print "The $number is a +/- integer\n"; } elsif ($number =~ /^[+-]?(?=\.?\d)\d*\.?\d*(?:e[+-]?\d+)?\z/i ) { print "The $number is a C float\n"; } elsif ($number =~ /\D/ ) { print "The $number has nondigits\n"; } } __END__ The 1 is a +/- integer The -1 is a +/- integer The 123.1 is a C float The 0.1 is a C float The Characters has nondigits

Which starts to make more sense.

Update 4: An alternative and possibly better solution would be to use Switch. It will give us the same output.

#!/usr/bin/perl use strict; use warnings; my @numbers = qw(1 -1 123.1 0.1 Characters); foreach my $number (@numbers) { checkInputStringWithSwitchConditions($number); } sub checkInputStringWithSwitchConditions { use Switch; switch ($_[0]) { case /^[+-]?\d+\z/ { print "The $_[0] is a + +/- integer\n" } case /^[+-]?(?=\.?\d)\d*\.?\d*(?:e[+-]?\d+)?\z/i { print "The $_[0 +] is a C float\n" } case /\D/ { print "The $_[0 +] has nondigits\n" } else { print "Non of the c +ases where True\n" } } } __END__ The 1 is a +/- integer The -1 is a +/- integer The 123.1 is a C float The 0.1 is a C float The Characters has nondigits

Update 5: By comparing the two methods together by using Benchmark, we can see that the Switch method is a better and faster method to use.

#!/usr/bin/perl use strict; use warnings; use Benchmark qw( timethese cmpthese ) ; my @numbers = qw(1 -1 123.1 0.1 Characters); my $swithRefLoop = 'foreach my $number (@numbers) { checkInputStringWithSwitchConditions($number); }'; my $ifAndElsIfRefLoop = 'foreach my $number (@numbers) { checkInputStringWithIfAndElseConditions($number); }'; my $Benchmark = timethese( -10, { IfAndElsIfLoop => $ifAndElsIfRefLoop, SwitchLoop => $swithRefLoop, } ); cmpthese $Benchmark; sub checkInputStringWithSwitchConditions { use Switch; switch ($_[0]) { case /^[+-]?\d+\z/ { print "The $_[0] is a + +/- integer\n" } case /^[+-]?(?=\.?\d)\d*\.?\d*(?:e[+-]?\d+)?\z/i { print "The $_[0 +] is a C float\n" } case /\D/ { print "The $_[0 +] has nondigits\n" } else { print "Non of the c +ases where True\n" } } } sub checkInputStringWithIfAndElseConditions { if ( $_[0] =~ /^[+-]?\d+\z/ ) { print "The $_[0] is a +/- integer\n"; } elsif ( $_[0] =~ /^[+-]?(?=\.?\d)\d*\.?\d*(?:e[+-]?\d+)?\z/i ) { print "The $_[0] is a C float\n"; } elsif ( $_[0] =~ /\D/ ) { print "The $_[0] has nondigits\n"; } } __END__ Benchmark: running IfAndElsIfLoop, SwitchLoop for at least 10 CPU seco +nds... IfAndElsIfLoop: 11 wallclock secs (10.04 usr + -0.00 sys = 10.04 CPU) +@ 4954765.54/s (n=49745846) SwitchLoop: 9 wallclock secs (10.04 usr + 0.00 sys = 10.04 CPU) @ 56 +91789.84/s (n=57145570) Rate IfAndElsIfLoop SwitchLoop IfAndElsIfLoop 4954766/s -- -13% SwitchLoop 5691790/s 15% --

Thank you all for your time and effort reading and replying to my question.

Seeking for Perl wisdom...on the process of learning...not there...yet!
  • Comment on [SOLVED] How do I determine with a regular expression whether a scalar is a number/whole/integer/float?
  • Select or Download Code

Replies are listed 'Best First'.
Re: How do I determine with a regular expression whether a scalar is a number/whole/integer/float?
by Marshall (Canon) on Jul 12, 2015 at 04:45 UTC
    I am curious as to why you even want/need to do this?

    It might be that you are asking either a home work question or the wrong question for a practical application. I am having trouble understanding your application that requires this distinction between C data types.

    If you want to know if some string is valid in a numeric context, let Perl figure it out by simply adding "zero" to that number. If that is not a valid number, eg. "1.1.1", you will get a warning error message.

    How to intercept this warning message and do something about it is different than what you have asked. Anyway some very simple code is attached that will generate a warning message if a "number" is not a "number"...

    #!/usr/bin/perl use strict; use warnings; $| =1; #turn off STDIO buffering #this causes the error messages to STDERR #to appear closer to the standard print stuff my @numbers = qw(1 -1 123.1 0.1 1E6 ); foreach my $number (@numbers) { print "$number: \t", $number+0,"\n"; } print "now weirder stuff...\n\n"; @numbers = qw (1.1.1 4-1-1 --2); foreach my $number (@numbers) { print "$number: \t", $number+0,"\n"; } __END__ OUTPUT: 1: 1 -1: -1 123.1: 123.1 0.1: 0.1 1E6: 1000000 now weirder stuff... Argument "1.1.1" isn't numeric in addition (+) at C:\PerlTemp\testnume +ric2.pl line 19. 1.1.1: 1.1 Argument "4-1-1" isn't numeric in addition (+) at C:\PerlTemp\testnume +ric2.pl line 19. 4-1-1: 4 Argument "--2" isn't numeric in addition (+) at C:\PerlTemp\testnumeri +c2.pl line 19. --2: 0

      Hello Marshall,

      I want to be able to capture and understand different numbers coming in, not strings. I know that there are many examples how to check if the string is a number or an integer for example (A function to determine if a string is numeric).

      I know that there are modules for this purpose (Scalar::Util).

      But my goal is that I want with the use of a regular expression to determine what "category" is e.g. (integer, decimal, float).

      This is the reason that I define my array my @numbers = (1, -1, 123.1, 0.1); without the qw so the values will not be strings but numbers.

      Thank for you for your time and effort reading and replying to my question.

      Seeking for Perl wisdom...on the process of learning...not there...yet!

        But my goal is that I want with the use of a regular expression to determine what "category" is e.g. (integer, decimal, float).

        This is the reason that I define my array my @numbers = (1, -1, 123.1, 0.1); without the qw so the values will not be strings but numbers.

        What does a regular expression match against? Yup, a string.

        So, no matter how you initialize your array, for the purpose of comparison against a pattern, its elements are converted into strings.

        All conversions from string to number, from number to string, from integer to float and so on are done under the hood by perl, if necessary.

        Read perldata. - A scalar value (SV) has various slots: IV (integer), PV (string pointer), NV (numeric value). The currently active slot is determined by the FLAGS field of the scalar:

        use Devel::Peek; $c = 1; Dump $c; $c = "2"; Dump $c; $c = 3.141592653; Dump $c; __END__ SV = PVNV(0x176e4d0) at 0x1776f20 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 1 NV = 1.23456789 PV = 0 SV = PVNV(0x176e4d0) at 0x1776f20 REFCNT = 1 FLAGS = (POK,pPOK) IV = 1 NV = 1.23456789 PV = 0x17a7900 "2"\0 CUR = 1 LEN = 16 SV = PVNV(0x176e4d0) at 0x1776f20 REFCNT = 1 FLAGS = (NOK,pNOK) IV = 1 NV = 3.141592653 PV = 0x17a7900 "2"\0 CUR = 1 LEN = 16

        Note that after assigning $c the (shortened ;-) value of PI, the other slots - IV, PV - retain their previous values, but the FLAGS field now refers to NV: (NOK,pNOK).

        Incrementing the string " 2 ", which to perl looks like a number, results in the allocation of a integer slot which holds the value 3 - and adding 0.141592653 results in the allocation of a NV slot:

        use Devel::Peek; $c = " 2 "; Dump $c; $c++; Dump $c; $c+= 0.141592653; Dump $c; __END__ SV = PV(0x13f85f0) at 0x17c2de8 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0x17e5820 " 2 "\0 CUR = 3 LEN = 16 SV = PVIV(0x1171478) at 0x17c2de8 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 3 PV = 0x17e5820 " 2 "\0 CUR = 3 LEN = 16 SV = PVNV(0x17ba4d0) at 0x17c2de8 REFCNT = 1 FLAGS = (NOK,pNOK) IV = 3 NV = 3.141592653 PV = 0x17e5820 " 2 "\0 CUR = 3 LEN = 16
        perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: How do I determine with a regular expression whether a scalar is a number/whole/integer/float?
by BrowserUk (Patriarch) on Jul 11, 2015 at 23:22 UTC

    The output you've posted did not come from the code that you posted.

    Running the code you posted, (second code block) under 5.10 and 5.18 I get this output:

    The 1 is a whole number The 1 is a whole number The 123.1 has nondigits The 0.1 has nondigits

    I suspect the code you actually ran to get the output you posted included:

    my @numbers = qw(1, 1.0, 123.1, 0.1);

    And did not have warnings enabled.

    But that doesn't explain all the differences.

    Try posting the actual code you run; and the actual output you get.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
    I'm with torvalds on this Agile (and TDD) debunked I told'em LLVM was the way to go. But did they listen!

      Hello BrowserUk,

      You are right, I modified the question while you where trying to answer it. I am sorry for that. I noticed that I was testing two number 1 and 1.0 which will give the exact output.

      That is why I modified the code again. I have updated the testing code and also the output.

      Thank you for your time and effort reading and assisting me with my question.

      Seeking for Perl wisdom...on the process of learning...not there...yet!
Re: How do I determine with a regular expression whether a scalar is a number/whole/integer/float?
by kcott (Archbishop) on Jul 11, 2015 at 23:17 UTC

    G'day thanos1983,

    The code you've posted is not the code you're running!

    ... and as I'm replying to you, I see you're changing your post without showing any update.

    Your first line of output was originally:

    The 1, has nondigits

    Now you've changed it to:

    The 1 is a whole number

    Please read: "How do I change/delete my post?".

    -- Downvoted, lost interest, moving on, won't be returning.

    -- Ken

      G'day kcott

      You are right, I updated the code on minor points, while you where trying to assist me. I did when I figure out that I was testing two number 1 and 1.0 that did not make any sense to test.

      Apologies for that that you lost interest. I hope that this will not happen again on my questions.

      Thank you for spending time and trying to assist.

      Seeking for Perl wisdom...on the process of learning...not there...yet!
Re: How do I determine with a regular expression whether a scalar is a number/whole/integer/float?
by Laurent_R (Canon) on Jul 12, 2015 at 10:27 UTC
    A couple of comments.

    The code you quote from the FAQ and the code you wrote for testing purposes are not equivalent at all. The FAQ's code will try each regex in turn, whereas your code will skip all the subsequent tests as soon as one of them is successful. So that if you test for example with -1 or 1.3, the first regex (has non digits) will match and no other test will be carried out (because they are all in an else branch of the first condition). In brief, your algorithm is wrong.

    Then you also have an error in your update:

    my @numbers = qw(1, 1.0, 123.1, 0.1);
    is wrong because you should not put commas in the qw// operator (see http://perldoc.perl.org/perlop.html#Quote-Like-Operators). With the above syntax, the first elements of @numbers are "1,", "1.0,", etc., so that all your elements except the last have at least one non digit, the trailing comma. You should write:
    my @numbers = qw(1 1.0 123.1 0.1);

      Hello Laurent_R,

      Well I assumed that as soon it will match the if condition it will break. and this is the reason that I should modify it.

      Regarding the array, I do not want the array to contain strings I want the array to contain numbers. This is why I define it like this my @numbers = (1, 1.0, 123.1, 0.1);. I verified the syntax from the tutorial Arrays: A Tutorial/Reference.

      Seeking for Perl wisdom...on the process of learning...not there...yet!
        Well I assumed that as soon it will match the if condition it will break. and this is the reason that I should modify it.
        I am not sure that I understand correctly what you are saying, and I am not sure that you understood what I was saying.

        The FAQ's original code had continue statements so that every condition is checked, even if a previous condition succeeded. So that the FAQ code, presented with 1.0 would report a non digit and then also report a real number. Your code does not do that.

        For your nested if ... elsif ... else statements to work properly, you need to reorder your conditions, for example: 1. check if it is a positive integer; 2. check for a negative integer; 3. check for a (positive or negative) float; 4. check for non-digits other than those we have accepted when checking for negative and floats.

        my @numbers = (1, 1.0, 123.1, 0.1);
        is certainly a good way of declaring an array of numbers, but I was only stressing that your use of qw/.../ was incorrect because of the commas.
Re: How do I determine with a regular expression whether a scalar is a number/whole/integer/float?
by CountZero (Bishop) on Jul 12, 2015 at 11:45 UTC
    Don't reinvent the weel, but have a look at Regexp::Common and Regexp::Common::number which has regular expressions for integers (both signed and unsigned and different bases), reals (or floating numbers in signed or unsigned format in different bases and even in "scientific notation"), decimal, octal and hexadecimal numbers and Roman numbers.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

    My blog: Imperial Deltronics

      Hello CountZero,

      I was trying to avoid using modules, but maybe at the end I will. Thanks for pointing out some more options.

      Seeking for Perl wisdom...on the process of learning...not there...yet!
        Why would you try to avoid using modules? It is one of the major strengths of Perl, envied by many other languages.

        CountZero

        A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

        My blog: Imperial Deltronics
Re: [SOLVED] How do I determine with a regular expression whether a scalar is a number/whole/integer/float?
by ikegami (Patriarch) on Jul 14, 2015 at 17:00 UTC
    You used elsif ($number =~ /^\d+\z/ ) {, but the perlfaq4 used the equivalent of if ($number =~ /^\d+\z/ ) {.