Regex to validate numbers 0-100 with up to two decimal places - php

So I know it would be easier to just use the php is_numeric function, but what i'm trying to do is create a regular expression, mostly for learning regex, to test test scores. The range can be 0-100, but can have 2 decimal places. This is what I have so far and its not working.
if (preg_match("^([0-9]){2,3})(.[0-9]{2})?$([0-9]{1})?$", $quiz1))
{
echo "match";
}
else {
$error = true;
}
If I'm thinking correctly the literal meaning:
start of string find characters 0-9, for two places.
optional end of string decimal and find characters 0-9, for two places.
optional end of string find characters 0-9, for 1 place.

Why not something like this?
/^(?:100|\d{1,2})(?:\.\d{1,2})?$/
^ - beginning of string
(?:100|\d{1,2}) - non-capturing group, 100 or 0-99
(?:.\d{1,2})? - optional non-capturing group (.# or .##)
$ - end of string
Results:
php > $tests = array(0, 5, 10, 50, 100, 99.5, 75.43, 75.436, 101);
php > foreach ($tests as $test) { print $test . " - " . preg_match("/^(?:100|\d{1,2})(?:\.\d{1,2})?$/", $test) . "\n"; }
0 - 1
5 - 1
10 - 1
50 - 1
100 - 1
99.5 - 1
75.43 - 1
75.436 - 0
101 - 0
75F43 - 0
Yours doesn't work even when I add the slashes and remove the extra ).
php > foreach ($tests as $test) { print $test . " - " .
preg_match("/^([0-9]{2,3})(.[0-9]{2})?$([0-9]{1})?$/", $test) . "\n"; }
0 - 0
5 - 0
10 - 1
50 - 1
100 - 1
99.5 - 0
75.43 - 1
75.436 - 0
101 - 1
75F43 - 1

Surround your expression with / characters
http://php.net/manual/en/regexp.reference.delimiters.php

gpojd's answer is correct. However, here's why your regex isn't working.
First of all, you want the first $ inside the ()'s. Because otherwise it will need to match the end of the string, and then later after that the end of the string again, which is of course impossible.
Second, the dot character needs to become \. because a dot by itself matches any character, but you want an actual dot. Lastly, you need delimiters, like someone else suggested. Here's what your regex matches:
first, two or three digits, mandatory.
Then, any character followed by two digits, optionally.
Then, the end of the string, mandatory.
Then, one digit, optionally.
Then, the end of the string again, mandatory.
Since you're a programmer, it can be hard to get used to the way of thinking with regular expressions; you're used to thinking in terms of if-else statements but regular expressions don't really work that way.
Again, the option above is good enough but if I were to write a regex for this I'd put:
/^100(.00)?|([1-9]?[0-9])(\.[0-9]{2})?$/
So, either 100 followed by an optional .00, or: first, an optional nonzero digit and then a mandatory digit (those make the numbers 0 to 99), then optionally a dot followed by two digits.

Related

Regular Expression for replace all non digit expect symbols

i can't figure out this thing i think it possible with only one pattern, please help me improve.
I have this string 2 / 3 items and i wont receive only 2 / 3
Items can also be write in cirillic so 2 / 3 штуки
So i think the best way is use \D all non digit (result 23)
But this delete also the slash that i want to keep, how i can do?
// this was my solution for now,
// but it not complete for cirillic cause i have an error
// it return: 2 / 3 �
// maybe is something with encoding?
preg_replace('#[a-zA-Zа-яА-Я]*#', '', '2 / 3 штуки');
// so i chose to do this, but doesn't know how to keep slash
preg_replace('#[\D]*#', '', '2 / 3 штуки');
// it return: 23
# How to get 2 / 3 ?
You can use
if (preg_match('~\d+\s*/\s*\d+~u', $text, $match)) {
echo $match[0];
}
Also, if the fraction part is optional, use
preg_match('~\d+(?:\s*/\s*\d+)?~u', $text, $match)
And if you need to extract all occurrences, use preg_match_all:
preg_match_all('~\d+(?:\s*/\s*\d+)?~u', $text, $matches)
See the regex demo and the PHP demo. Note that preg_match extracts the match rather than remove it (as is the case with preg_replace).
Pattern details
\d+ - one or more digits
- \s*/\s* - / enclosed with zero or more whitespaces
\d+ - one or more digits
Note that u is used in case the whitespace in your string can be other than regular ASCII whitespace, like \xA0.

Conditional regex length based on the first character

There is a string with numbers I need to validate with PHP preg_match.
If it starts with 10 or 20 or 30, I need 7 more numbers after the inital 2, but in any other cases I need 8 numbers only and don't care what are the lead characters.
The first part is the simple one
/^(1|2|3)0\d{7}$
But how can I add an ELSE part? There I need a simple
^\d{8}$
I need to match these examples:
101234567
201234567
12345678
33445566
You may use
^(?:[1-3]0\d{7}|(?![1-3]0)\d{8})$
See the regex demo
Details
^ - start of string
(?: - start of a non-capturing group:
[1-3]0\d{7} - 1, 2 or 3, then 0 and any 7 digits
| - or
(?![1-3]0)\d{8} - no 10, 20 or 30 immediately at the start of the string are allowed, then any 8 digits are matched
) - end of the group
$ - end of the string.
Here's an alternative using (?(?=regex)then|else) aka conditionals:
^(?(?=[1-3]0)[1-3]0\d{7}|\d{8})$
It literally says: if [1-3]0 is right at the start, match [1-3]0\d{7}, else match \d{8}.
Demo: https://regex101.com/r/LXoHyk/1 (examples shamelessly taken from Wiktor's answer)

Combine two regular expressions for php

I have these two regular expression
^(((98)|(\+98)|(0098)|0)(9){1}[0-9]{9})+$
^(9){1}[0-9]{9}+$
How can I combine these phrases together?
valid phone :
just start with : 0098 , +98 , 98 , 09 and 9
sample :
00989151855454
+989151855454
989151855454
09151855454
9151855454
You haven't provided what passes and what doesn't, but I think this will work if I understand correctly...
/^\+?0{0,2}98?/
Live demo
^ Matches the start of the string
\+? Matches 0 or 1 plus symbols (the backslash is to escape)
0{0,2} Matches between 0 and 2 (0, 1, and 2) of the 0 character
9 Matches a literal 9
8? Matches 0 or 1 of the literal 8 characters
Looking at your second regex, it looks like you want to make the first part ((98)|(\+98)|(0098)|0) in your first regex optional. Just make it optional by putting ? after it and it will allow the numbers allowed by second regex. Change this,
^(((98)|(\+98)|(0098)|0)(9){1}[0-9]{9})+$
to,
^(?:98|\+98|0098|0)?9[0-9]{9}$
^ this makes the non-grouping pattern optional which contains various alternations you want to allow.
I've made few more corrections in the regex. Use of {1} is redundant as that's the default behavior of a character, with or without it. and you don't need to unnecessarily group regex unless you need the groups. And I've removed the outer most parenthesis and + after it as that is not needed.
Demo
This regex
^(?:98|\+98|0098|0)?9[0-9]{9}$
matches
00989151855454
+989151855454
989151855454
09151855454
9151855454
Demo: https://regex101.com/r/VFc4pK/1/
However note that you are requiring to have a 9 as first digit after the country code or 0.

An easier way to match a pattern either x or y times

I am trying to write a regex to validate a numerical code. The code can be one of a few valid lengths, but not all of the lengths between. I know I can do something like
preg_match("/^([0-9]{".$x."}|[0-9]{".$y".})$/", "$string")
but would rather not have to repeat the subpattern for each valid length. Especially as my actual regular expression is already going to be on the complex side.
preg_match("/^[0-9]{".$x.",".$y."})$/", "$string")
obviously won't work for me, as it would also match any number of digits between $x and $y.
Is their an easier way to use a regex to match a pattern either x or y times?
Edit:
While my complete regex is a bit complex, the portion that can be repeated x or y times is very simple [0-9], so answers like the ones given by sln and barmar, while interesting, will not solve the problem in this particular case.
Especially as my actual regular expression is already going to be on the complex side.
Then this is the only alternative available in all of Regular Expression land.
I guess since this is PHP, you can always put a singular unit in a function, then call the function using a range quantifier in a series of alternations...
You can rename the function's a little less descriptive, like A, B, or C ...
The big advantage is that you can inject other separator code, for instance
(?:(?&digit)\s*){4} or whatever you want.
# \b(?:(?&digit){4}|(?&digit){7}|(?&digit){9}|(?&digit){11})\b(?(DEFINE)(?<digit>[0-9]))
\b # add a boundary here
(?:
(?&digit){4} # match 4 times
| (?&digit){7} # or, match 7 times
| (?&digit){9} # or, match 9 times
| (?&digit){11} # or, match 11 times
)
\b # add a boundary here
(?(DEFINE) # Add complex expressions here
(?<digit> [0-9] ) # (1)
)
Input: 1234 1234567 123456789
Output:
** Grp 0 - ( pos 0 , len 4 )
1234
** Grp 0 - ( pos 5 , len 7 )
1234567
** Grp 0 - ( pos 13 , len 9 )
123456789
Put the complex pattern in a variable, then use that in the regexp.
$pat = 'complex pattern';
preg_match("/^($pat{" . $x . "}|$pat{" . $y . "})$/", $string);
Note, by the way, that I had to use concatenation rather than substitution to get $x and $y inside the {} in the regexp. This is because {$var} is the syntax for "complex variable substitution" in PHP strings, so the {} will not appear in the resulting string. See the PHP documentation on double quoted strings.

Bug in a PHP regex

I am trying to print "1" if there are at least two of the same figure in the match, else 0.
What is wrong in the regex?
if ( max ( array_map ('strlen', preg_split('/([0-9])[^0-9]*\1/', "1 2 3 1 4") ) ) == 1 )
echo 1;
else
echo 0;
echo preg_match('/(?<=^|[^0-9])([0-9)+)(?=[^0-9]).*(?<=[^0-9])\1(?=[^0-9]|$)/', "1 2 3 1 4");
Will match for any repeated number in the sequence, and echo 1 if there is something repeated, 0 if not.
(Original version just looked for something repeated after each other, this matches repeated anywhere in the string)
The [^0-9]* matches any number of NON-digit characters. So if there's another number, it will fail the match. Try replacing [^0-9]* with a simple .*, which will match digits or non-digits.
Try the following code. It should print 1 when there is a repeat number.
if(0 == strlen(preg_replace('/.*([0-9]).+\1.*/', '', '1 2 3 1 5 4')))
echo 1;
else echo 0;
The regex /.*([0-9]).+\1.*/ will match a number and another number (with .+ or anything in between them).
Hope this helps.
Try a lookahead assertion. I use "The Regex Coach" whenever I'm trying to figure something out. It doesn't give you hints or anything, but it does give immediate feedback.
Test string: "1 2 3 1 4 3"
Regex: ([0-9])(?=.*\1)
Basically the ()'s around the [0-9] store the result, and the lookahead (?= matches .* - any character and then \1 - what was matched first (so it looks for any number and then looks ahead to see if that number occurs again)
This will match both "1" and "3"
I'm not quite sure if php supports lookaheads, but that's my take.

Categories