9

I recently found out that a method I've been using for validating user input accepts some values I'm not particularly happy with. I need it to only accept natural numbers (1, 2, 3, etc.) without non-digit characters.

My method looks like this:

function is_natural($str)
{
   return preg_match('/[^0-9]+$/', $str) ? false : $str;
}

So it's supposed to return false if it finds anything else but a whole natural number. Problem is, it accepts strings like "2.3" and even "2.3,2.2"

intcreator
  • 4,206
  • 4
  • 21
  • 39
soren.qvist
  • 7,376
  • 14
  • 62
  • 91

4 Answers4

28

perhaps you can clarify the difference between a "number" and a "digit" ??

Anyways, you can use

if (preg_match('/^[0-9]+$/', $str)) {
  // contains only 0-9
} else {
  // contains other stuff
}

or you can use

$str = (string) $str;
ctype_digit($str);
CrayonViolent
  • 32,111
  • 5
  • 56
  • 79
  • I think I misunderstood digits. It needs to accept digits only. Looks like ctype_digit only accepts digits in string form, I need it to accept integers. – soren.qvist Feb 02 '11 at 18:24
  • yes, ctype_digit() will work, however note how I cast $str as a string first, because ctype_digit() will always return false if you pass an integer to it. – CrayonViolent Feb 02 '11 at 18:25
  • ctype_digit() still returns TRUE on strings like '2.2,2.3' – soren.qvist Feb 02 '11 at 18:29
  • 1
    if ctype_digit() returns true for '2.2,2.3' then it's a PHP bug that should be raised on bugs.php.net, but I can't replicate this fault – Mark Baker Feb 02 '11 at 18:32
  • 1
    **Read carefully**: `/^[0-9]+$/` accepts `0`. I recommend you should use `/^[1-9][0-9]*$/` instead. – Константин Ван Feb 29 '16 at 07:45
  • @K._ setting aside the fact that you are commenting on a 5+ year old post.. I'm not sure what you are getting at.. are you suggesting that `0` isn't a number? – CrayonViolent Feb 29 '16 at 15:59
  • 1
    @CrayonViolent It doesn't matter how old is the post, since Google can lead someone(like me) to this post. Umm, anyway. What I want to say is the fact that the questioner was looking for a regular expression for a **natural** number(ℕ). Please read this post: [Is 0 a natural number?](http://math.stackexchange.com/questions/283/is-0-a-natural-number) – Константин Ван Feb 29 '16 at 19:31
  • @K._ Okay fair enough about time past, but I did read the post - and the followup comments in various places, about what the OP *really* wanted. Did you? It is clear from both the question and followup comments that OP did not understand what he actually wanted. – CrayonViolent Feb 29 '16 at 22:56
  • @CrayonViolent See, ‘I need it to only accept **natural** numbers (**1, 2, 3, etc.**) without non-digit characters.’; this is what the questioner said on the post and also the title. If you think it's not what the questioner _really_ wanted, then why don't you make him edit the post? Comments in Stack Overflow are not _only_ for you, nor the questioner. Did you consider someone who will read this post? – Константин Ван Mar 01 '16 at 08:40
  • @K._ and then the very next part of that question was "without non-digit characters" which makes no sense. And that still doesn't count the followup comments. Look man, I will agree that things could be edited/consolidated to fit how things actually went down with this thread but again, you are nickle and diming some random question that happened a long time ago, and you're hollering at someone that doesn't really have anything to do with making sure things get edited. Get some perspective. I'm done with this conversation. GLHF – CrayonViolent Mar 01 '16 at 14:28
26

The problem with /^[0-9]+$/ is that it also accepts values like 0123. The correct regular expression is /^[1-9][0-9]*$/.

ctype_digit() suffers the same problem.

If you also need to include zero use this regex instead: /^(?:0|[1-9][0-9]*)$/

Koraktor
  • 41,357
  • 10
  • 69
  • 99
  • 1
    There is nothing particularly harmful about accepting 0123. Kinda depends on what he's expecting overall, what he's intending to do with it...but for example, 0123 will evaluate as 123 in calculations etc... – CrayonViolent Feb 02 '11 at 18:28
  • 8
    A "natural number" is every ordinary number from 1 upwards (ℕ). That's why I think "0123" is not valid. – Koraktor Feb 02 '11 at 18:30
  • true, but as OP pointed out, he was confused about definitions. So again, it boils down to what he's wanting to actually use it for.. – CrayonViolent Feb 02 '11 at 18:33
  • Leading zeros left of the decimal point are no more significant than trailing zeros right of the decimal point. – tchrist Feb 02 '11 at 18:34
  • 1
    @tchrist They're significant in cases where the number parser tries to autodetect octal. This means `03` = 3, `11` = 11, but `011` = 9. – Damian Yerrick Jan 23 '15 at 18:10
2

Use ctype_digit() instead

Mark Baker
  • 209,507
  • 32
  • 346
  • 385
-1

I got an issue with ctype_digit when invoice numbers like "000000196" had to go through ctype_digit.

So I have used a:

if (preg_match('/^[1-9][0-9]?$/', $str)) {
  // only integers
} else {
  // string
}
  • This question is seven years old and it already has an accepted, seven-year old answer. What's more, the pattern you use has already been suggested by other users. – baduker Mar 30 '18 at 07:41