Reputation: 568
I'm trying to make a regular expression that validates the following format:
Example that I would consider valid:
4,31,2-22,8
29,1-10,2-12,9
Example that I would consider invalid:
4,31,2-22,8,
29,1-10,-2-12-,9
29,1-50,12-2,32
The regular expression that I have so far is the following:
(((1[0-9]|2[0-9]|3[0-1]|[1-9])(\-(1[0-9]|2[0-9]|3[0-1]|[1-9]))?)(\,((1[0-9]|2[0-9]|3[0-1]|[1-9])(\-(1[0-9]|2[0-9]|3[0-1]|[1-9]))?))*)
At the moment this expression takes me well the "-" and ",", and that the numbers go from 1 to 31. The problem of the rank that the second value is greater than the first I have no idea of how to solve it. Any suggestions?
Upvotes: 2
Views: 170
Reputation: 24276
As Jeff already suggested I wouldn't use regex because it would be very hard to understand.
The solution is much simpler than it seems:
function isValid($string)
{
$numbers = explode(',', $string);
// Covers the case when you have an empty value at the beginning/end of string.
if (count($numbers) !== count(array_filter($numbers))) {
return false;
}
foreach ($numbers as $number) {
if (is_numeric($number) && $number >= 1 && $number <= 31) {
continue;
}
if (!preg_match('/^(\d+)-(\d+)$/', $number, $matches)) {
return false;
}
if ($matches[1] >= 1 && $matches[1] <= 31 &&
$matches[2] >= 1 && $matches[2] <= 31 &&
$matches[2] > $matches[1]
) {
continue;
}
return false;
}
return true;
}
$strings = [
'4,31,2-22,8',
'29,1-10,2-12,9',
'4,31,2-22,8,',
'29,1-10,-2-12-,9',
'29,1-50,12-2,32',
];
foreach ($strings as $string) {
var_dump(isValid($string));
}
The results would be:
bool(true)
bool(true)
bool(false)
bool(false)
bool(false)
Upvotes: 1
Reputation: 147146
I think it's best to do this with a combination of regex and regular code. This function checks to see if the entire string matches a pattern of numbers or ranges separated by commas, then extracts the individual numbers or ranges and applies the other error checking (between 1 and 31, end >= start) to them:
function validate_range($range) {
if (!preg_match('/^((\d+(?:-\d+)?)(?:,(?!$)|$))+$/', $range)) return false;
preg_match_all('/(\d+(?:-\d+)?)/', $range, $matches);
foreach ($matches[1] as $match) {
if (strpos($match, '-') !== false) {
list($start, $end) = explode('-', $match);
if ($end < $start) return false;
if ($start < 1 || $start > 31 || $end < 1 || $end > 31) return false;
}
if ($match < 1 || $match > 31) return false;
}
return true;
}
You can test it like this:
$ranges = array(
'4,31,2-22,8',
'29,1-10,2-12,9',
'4,31,2-22,8,',
'29,1-10,-2-12-,9',
'29,1-50,12-2,32');
foreach ($ranges as $range) {
if (validate_range($range))
echo "$range is OK!\n";
else
echo "$range is no good\n";
}
Output:
4,31,2-22,8 is OK!
29,1-10,2-12,9 is OK!
4,31,2-22,8, is no good
29,1-10,-2-12-,9 is no good
29,1-50,12-2,32 is no good
Upvotes: 1