PHP regular expression for numbers and commas only - php

I need to create a regular expression to validate comma separated numeric values.
They should look like: 1,2,3,4,5 etc....
The value must be either a single digit like: 1 no empty spaces before or after, no commas before or after.
Or... multiple numerical values separated by commas. First and last characters must be a number.
I have the following code but it only checks for numbers and commas in no particular order:
How can I change the regular expression below to fit the above description?
Thank you!
// get posted value
if(isset($_POST['posted_value']))
{
$sent_value = mysqli_real_escape_string($conn, trim($_POST['posted_value']));
if(preg_match('/^[0-9,]+$/', $posted_value))
{
$what_i_need = $posted_value;
}
else
{
$msg .= $not_what_i_need;
}
}
else
{
$msg .= $posted_value_not_set;
}

This should do it:
/^\d(?:,\d)*$/
Explanation:
/ # delimiter
^ # match the beginning of the string
\d # match a digit
(?: # open a non-capturing group
, # match a comma
\d # match a digit
) # close the group
* # match the previous group zero or more times
$ # match the end of the string
/ # delimiter
If you allow multi-digit numbers, then change \d to \d+.

Do you allow the user to input numbers with commas in them, like 5,000 for example? what about decimals like 5.6?
Alternatively you could validate the input using explode something like this.
$values = explode(',',$sent_value);
$valid = true;
foreach($values as $value) {
if(!ctype_digit($value)) {
$valid = false;
break;
}
}

Related

How to make regex that wouldn't allow anything except numbers separated by a comma? [duplicate]

This question already has answers here:
How to validate that a string only contain lowercase letters?
(3 answers)
Closed 4 years ago.
I have a regex, which is: /[0-9]+(,[0-9]+)*/.
It accepts only numbers, separated by a comma. For instance:
1,2,3,4 = good
,1,2,3,4 = bad
1,2,3,4, = bad
String = bad
But it also accepts a numbers with a space:
1, 2, 3, 4 = good
How to make regex that wouldn't allow to do so and would only let to enter a numbers, separated by a comma without spaces?
This should work:
/^\d(,\d)*$/
^ match the beginning of the string
\d match a digit
$ match
the end of the string
Example:
if (preg_match("/^\d(,\d)*$/", $string))
echo "good";
else
echo "bad";
Your regex is correct as is, you just need to use anchors so you check the full string, and not a partial match.
^[0-9]+(,[0-9]+)*$
https://regex101.com/r/zU22VX/1/
An alternative approach could be:
$string = '1,2,3,4';
$array = str_getcsv($string);
foreach($array as $number) {
if(!is_numeric($number)) {
echo 'Bad value' . $number;
break;
}
}
Use this:
^(\d,(?=\d))*\d$
How it works:
^: match strings which starts with..(what follows)
(...)*: what inside the brackets can be repeated any number of times
\d,(?=\d): this is the block that is allowed to repeat. It looks for a number from 0 to 9 followed by a comma which is followed by a number. The number following the comma is not included in the match.
\d$: finally, the string must terminate with a number.

How can I search for duplicate numbers next to each other?

I'm trying to use preg_match() in PHP to make a pattern match for 4 digit numbers that have this pattern:
0033
1155
2277
Basically the first 2 digits are the same and the last 2 digits are the same, but not all 4 digits are the same.
Is preg_match() even the correct function to use? Or should I just split them up and match that way?
To search this kind of number in a text you can use:
preg_match('~\b(\d)\1(?!\1)(\d)\2\b~', $str, $m)
(or with preg_match_all if you want all of them)
details:
~ # pattern delimiter
\b # word boundary (to be sure that 1122 isn't a part of 901122)
(\d) # capture the first digit in group 1
\1 # back-reference to the group 1
(?!\1) # negative lookahead: check if the reference doesn't follow
(\d) #
\2 #
\b # word boundary (to be sure that 1122 isn't a part of 112234)
~ #
If you want to check if an entire string is the number, use the string limit anchors in place of word boundaries:
~\A(\d)\1(?!\1)(\d)\2\z~
You could use something like array_filter with a comparison callback:
function compare($number) {
// Compare first 2 numbers
if (intval($number[0]) !== intval($number[1])) {
return false;
}
// Compare last 2 numbers
if (intval($number[2]) !== intval($number[3])) {
return false;
}
// Make sure first and last numbers aren't the same
if (intval($number[0]) === intval($number[3])) {
return false;
}
return true;
}
$data = array_filter($data, 'compare');
You could also do this using a closure:
$data = array_filter($data, function($number) {
return intval($number[0]) === intval($number[1]) && intval($number[2]) === intval($number[3]) && intval($number[0]) !== intval($number[3]);
});
Examples here: http://ideone.com/0VwJz8

Problems with preg_match

I'm using preg_match as a way to validate inputs on a form. Specifically, I am trying to validate input of currency. Here is the function:
if (preg_match("/^\$(((\d{1,3},)+\d{3})|\d+)\.\d{2}$/i", $inString)) {
return True;
} else {
return False;
}
I can get this to work AT ALL. I keeps returning False regardless of what I feed it (including valid strings). I'm sure I'm doing something obviously wrong, but I can't see it. You know how it is...
Anyone have any suggestions?
Try something like this:
$inString = '1550.50';
if (preg_match("/\b\d{1,3}(?:,?\d{3})*(?:\.\d{2})?\b/", $inString)) {
echo "True";
} else {
echo "False";
}
explanation:
\b # word boundary assertion
\d{1,3} # 1-3 digits
(?: # followed by this group...
,? # an optional comma
\d{3} # exactly three digits
)* # ...any number of times
(?: # followed by this group...
\. # a literal dot
\d{2} # exactly two digits
)? # ...zero or one times
\b # word boundary assertion
The preg_match function already returns True or False depending on whether it matches, so there is no need to return True or False a second time.
This means you can directly echo the values of True or False:
$inString = "$12.50";
$price_regex = '~^\$(((\d{1,3},)+\d{3})|\d+)\.\d{2}$~';
echo preg_match($price_regex, $inString);
// echoes 1
You can also directly return these values:
return preg_match($price_regex, $inString);
You can perform a Boolean test:
if( preg_match($price_regex, $inString) ) { // take some action }
else { // take another action }
If what you want instead is to echo some value depending on whether there is a match, do this:
echo (preg_match($price_regex, $inString)) ? "**It Matches!**" : "Nah... No match." ;
Notes:
Changed the delimiter to ~ (more legible)
Removed the i flag (there are no letters, so it doesn't need to be case-insensitive)
Both answers given before this work, but here's an explanation of why the original preg_match pattern didn't work.
It's because the pattern is enclosed in double quotes. When PHP sees this, it treats whatever follows a $ sign as a variable name. If you need to include a literal $ sign inside a double quoted string, the dollar has to be preceded by a backslash.
so both of these patterns work:
'/^\$(((\d{1,3},)+\d{3})|\d+)\.\d{2}$/i'
"/^\\$(((\d{1,3},)+\d{3})|\d+)\.\d{2}\$/i"
Obviously, using single quotes is simpler in this case.

php regular expression minimum and maximum length doesn't work as expected

I want to create a regular expression in PHP, which will allow to user to enter a phone number in either of the formats below.
345-234 898
345 234-898
235-123-456
548 812 346
The minimum length of number should be 7 and maximum length should be 12.
The problem is that, the regular expression doesn't care about the minimum and maximum length. I don't know what is the problem in it. Please help me to solve it. Here is the regular expression.
if (preg_match("/^([0-9]+((\s?|-?)[0-9]+)*){7,12}$/", $string)) {
echo "ok";
} else {
echo "not ok";
}
Thanks for reading my question. I will wait for responses.
You should use the start (^) and the end ($) sign on your pattern
$subject = "123456789";
$pattern = '/^[0-9]{7,9}$/i';
if(preg_match($pattern, $subject)){
echo 'matched';
}else{
echo 'not matched';
}
You can use preg_replace to strip out non-digit symbols and check length of resulting string.
$onlyDigits = preg_replace('/\\D/', '', $string);
$length = strlen($onlyDigits);
if ($length < 7 OR $length > 12)
echo "not ok";
else
echo "ok";
Simply do this:
if (preg_match("/^\d{3}[ -]\d{3}[ -]\d{3}$/", $string)) {
Here \d means any digits from 0-9. Also [ -] means either a space or a hyphen
You can check the length with a lookahead assertion (?=...) at the begining of the pattern:
/^(?=.{7,12}$)[0-9]+(?:[\s-]?[0-9]+)*$/
Breaking down your original regex, it can read like the following:
^ # start of input
(
[0-9]+ # any number, 1 or more times
(
(\s?|-?) # a space, or a dash.. maybe
[0-9]+ # any number, 1 or more times
)* # repeat group 0 or more times
)
{7,12} # repeat full group 7 to 12 times
$ # end of input
So, basically, you're allowing "any number, 1 or more times" followed by a group of "any number 1 or more times, 0 or more times" repeat "7 to 12 times" - which kind of kills your length check.
You could take a more restricted approach and write out each individual number block:
(
\d{3} # any 3 numbers
(?:[ ]+|-)? # any (optional) spaces or a hyphen
\d{3} # any 3 numbers
(?:[ ]+|-)? # any (optional) spaces or a hyphen
\d{3} # any 3 numbers
)
Simplified:
if (preg_match('/^(\d{3}(?:[ ]+|-)?\d{3}(?:[ ]+|-)?\d{3})$/', $string)) {
If you want to restrict the separators to be only a single space or a hyphen, you can update the regex to use [ -] instead of (?:[ ]+|-); if you want this to be "optional" (i.e. there can be no separator between number groups), add in a ? to the end of each.
if (preg_match('/^(\d{3}[ -]\d{3}[ -]\d{3})$/', $string)) {
may it help you out.
Validator::extend('price', function ($attribute, $value, $args) {
return preg_match('/^\d{0,8}(\.\d{1,2})?$/', $value);
});

Extract part of string matching pattern - regex, close but no cigar

I have a string that can be very long and contain various lines and characters.
I am wanting to extract all lines that are surrounded by SB & EB:
SB1EB
SBa description of various lengthEB
SB123.456.78EB
SB99.99EB
SB99.99EB
SB2EB
SBanother description of various lengthEB
SB123.456.00EB
SB199.99EB
SB199.99EB
3
another description of various length that I don't want to return
123.456.00
599.99
599.99
SB60EB
SBanother description of various length that i want to keepEB
SB500.256.10EB
SB0.99EB
SB0.99EB
another bit of text that i don't want - can span multiple lines
This is the pattern I am using in PHP:
preg_match_all('/SB(\d+)EB\nSB(\w.*)EB\nSB(\d{3}\.\d{3}\.\d{2})EB\nSB(\d.*)EB\nSB(\d.*)EB\n/', $string, $matches)
So this should hopefully return:
[0] -> SB1EB
SBa description of various lengthEB
SB123.456.78EB
SB99.99EB
SB99.99EB
[1] -> SB2EB
SBanother description of various lengthEB
SB123.456.00EB
SB199.99EB
SB199.99EB
[2] -> SB60EB
SBanother description of various length that i want to keepEB
SB500.256.10EB
SB0.99EB
SB0.99EB
But I'm obviously doing something wrong because it isn't matching anything. Can somebody help please?
SOLUTION:
Based on #Sajid reply:
if (preg_match_all('/(?:SB.+?EB(?:[\r\n]+|$))/', $string, $result)) {
for($i=0;$i<count($result[0]);$i++){
$single_item = $result[0][$i];
$single_item = str_replace("SB","",$single_item);
$single_item = str_replace("EB","",$single_item);
if (preg_match('/(\d{3}\.\d{3}\.\d{2})/', $single_item)) {
$id = $single_item;
$qty = $result[0][$i-2];
$name = $result[0][$i-1];
$price = $result[0][$i+1];
$total = $result[0][$i+2];
}
}
}
It's a bit messy, but it works! :)
Thanks
A bit of a hack, but this will do the job:
$a = array();
if (preg_match_all('/(?:SB.+?EB(?:[\r\n]+|$)){5}/', $x, $a)) {
print_r($a);
}
Note that ?: is used to make the group non-capture, and the results will be in $a[0] (eg, $a[0][0], $a[0][1], $a[0][2] ...)
Based on #Sajid reply:
if (preg_match_all('/(?:SB.+?EB(?:[\r\n]+|$))/', $string, $result))
{
for ($i=0; $i<count($result[0]); $i++)
{
$single_item = $result[0][$i];
$single_item = str_replace("SB","",$single_item);
$single_item = str_replace("EB","",$single_item);
if (preg_match('/(\d{3}\.\d{3}\.\d{2})/', $single_item))
{
$id = $single_item;
$qty = $result[0][$i-2];
$name = $result[0][$i-1];
$price = $result[0][$i+1];
$total = $result[0][$i+2];
}
}
}
It's a bit messy, but it works! :)
preg_match_all('/SB\d+EB.*?(?=(?:SB\d+EB)|$)/s', $subject, $result, PREG_PATTERN_ORDER);
for ($i = 0; $i < count($result[0]); $i++) {
# Matched text = $result[0][$i];
}
So basically what I am doing (based on your input) is simply checking the "header" string SB\d+EB as an entry point and consuming everything until I find another "header" or the end of the input. Note the /s modifier so that . matches newlines.
Explanation:
# SB\d+EB.*?(?=(?:SB\d+EB)|$)
#
# Options: dot matches newline
#
# Match the characters “SB” literally «SB»
# Match a single digit 0..9 «\d+»
# Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+»
# Match the characters “EB” literally «EB»
# Match any single character «.*?»
# Between zero and unlimited times, as few times as possible, expanding as needed (lazy) «*?»
# Assert that the regex below can be matched, starting at this position (positive lookahead) «(?=(?:SB\d+EB)|$)»
# Match either the regular expression below (attempting the next alternative only if this one fails) «(?:SB\d+EB)»
# Match the regular expression below «(?:SB\d+EB)»
# Match the characters “SB” literally «SB»
# Match a single digit 0..9 «\d+»
# Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+»
# Match the characters “EB” literally «EB»
# Or match regular expression number 2 below (the entire group fails if this one fails to match) «$»
# Assert position at the end of the string (or before the line break at the end of the string, if any) «$»

Categories