PHP: Validate a given string is a valid number - php

I am trying to write a function that validates whether or not a given string is a valid number. I know I can use PHP is_numeric(), but the requirements is that the function needs to recognize commas as valid when:
Commas are in the whole number part of the number
Each comma has 3 whole number digits following it
At least one digit left of each comma
No more than 3 contiguous digits to the left of a comma
For instance:
It should recognize: 1,000,230 not ,021,201 or 1,023,12
It should also recognize positive and negative and dollar sign in front of it.
I am thinking to use preg_match to check the number format but I am not familiar with preg_match. Can you help me with this ? Any tip is appreciated ! Thank you !

No regex needed. Strip commas, then reformat with number_format. If that matches your original input, you're good.
if (number_format(str_replace(',', '', $number)) === $number) {
// pass
}
You can adjust how you want to handle decimals by providing a second argument to number_format().

You could use this function:
function hasNumericFormat($str) {
return preg_match("/^[-+]?[$]?([1-9]\d{0,2}(,\d{3})*|0)(\.\d+)?$/", $str);
}
Test code:
function test($str) {
if (hasNumericFormat($str)) {
echo "$str is OK<br>";
} else {
echo "$str violates numerical format<br>";
}
}
test("-$1,234,567.89"); // ok
test(",123,567.89"); // not ok: digit missing before comma
test("1,123,56"); // not ok: not 3 digits in last group
test("-0,123,567"); // not ok: first digit is zero
test("+0"); // ok
See it run on eval.in

The Intl library's NumberFormatter may be sufficient for you.
The NumberFormatter class
NumberFormatter::parse
NOTE: You may need to install or enable the Int library extension to use NumberFormatter class.
To enable the extension, check your php.ini file(C:\xampp\php\php.ini) and search for extension=intl. Remove the prepended ; if it exists. Restart Apache server and give it a try.
This extension is bundled with PHP as of PHP version 5.3.0.
/**
* Parse a number.
*
* #param string $locale - The locale of the formatted number.
* #param string $number - Formatted number.
* #return mixed - The value of the parsed number or FALSE on error.
*/
function parse_number(string $number, $locale)
{
return (new NumberFormatter($locale, NumberFormatter::DECIMAL))
->parse(trim($number));
}
echo parse_number('4.678.567.345,3827', 'de_DE'); // 4678567345.3827
echo parse_number('4.678567.345,3827', 'de_DE'); // FALSE
echo parse_number('4,678,567,345.3827', 'en_US'); // 4678567345.3827
echo parse_number(',678567,345.3827', 'en_US'); // FALSE

I will write it like this:
if (preg_match('~\A(?>[1-9][0-9]{0,2}(?:,[0-9]{3})*|0)(?:\.[0-9]+)?\z~', $number)) {
// true
} else {
// false
}
details:
~ # my favourite pattern delimiter
\A # start of the string
(?> # group for integer part:
# I made it atomic to fail faster when there aren't exactly 3 digits
# between commas
[1-9] [0-9]{0,2} # beginning doesn't start with a zero
(?:,[0-9]{3})* # zero or more 3 digits groups
| # OR
0 # zero alone
)
(?: # optional group for decimals
\.[0-9]+ # at least 1 digit
)?
\z # end of the string
~
Note that I have chosen to not allow numbers with a leading zero like 012, numbers without an integer part like .12 or integers with a dot like 12. but feel free to edit the pattern if it isn't what you want.

Related

Regular expression for amount always returns true

I'm not very familiar with regex. Can someone tell me why I always get true for result? In regex101 this works.. This is my input dd(crb_is_valid_amount( '1234567.'));
This is my function for validating amount:
function crb_is_valid_amount( $amount ) {
if ( preg_match( '/([\d]{1,6})(\.[\d]{2})?/', $amount ) )
{
return true;
}
return false;
}
You could use ^ and $ the specify the beginning and the end of the string
function crb_is_valid_amount( $amount ) {
return preg_match('/^\d{1,6}(\.\d{2})?$/', $amount) ;
}
var_dump(crb_is_valid_amount('112.12')); // 1
var_dump(crb_is_valid_amount('1234567.')); // 0
Your original regular expression returns true for the second case, because '1234567.' contains 6 digits, but doesn't take care of what's after the 6 first digits. Using ^ and $ checks if the given string matches exactly with the expression, from start to end.
Means:
^\d{1,6} # Begins with 1 to 6 digits,
(\.\d{2})? # Optionally with a dot and 2 digits,
$ # End of given string (nothing after accepted).
You should use beginning and ending of string anchors along with \d{1,6}
function crb_is_valid_amount($amount) {
return preg_match('/^\d{1,6}(?:\.\d{2})?$/', $amount) === 1 ? true : false;
}
Live demo

How to check a string for a given format using PHP?

I have many headlines in my project like:
00.00.2014 - Headline Description e.t.c.
I want to check with php if the given strings contain the format 00.00.0000 - in front. The part after the - doesn't matter.
Can someone help me with something like:
$format = '00.00.0000 -';
if ($string MATCHES $format IN FRONT) {
// ...some code...
}
This should work:
if (preg_match("/^\d{2}\.\d{2}\.\d{4}\s\-\s.*$/", $string) === 1) {
// $string matches!
}
Explanation:
^ is "the beginning of the string"
\d is any digit (0, 1, 2, ..., 9)
{n} means "repeated n times"
\. is a dot
\s is a space
\- is a minus sign
. is "any single character"
* means "repeated 0 or more times`
$ means "end of the string"
I don't have a dev environment to test this out on but i'll give you some psuedocode:
I'm unsure of the context, but you can test this function on any given STRING:
Function:
Boolean hasCorrectFormat($myString){
//Here take the string and cut it into a char array.
$charArray = str_split($myString);
//This will give you a char array. Compare the first 12 elements of this
//array to see if they are correct. If its supposed to be number make
//sure it is, if its supposed to be a "." make sure it is..etc
//"00.00.0000 -" is 12 characters.
if(!isNumeric(charArray[0])){
return false;
}
else if(!isNumeric(charArray[1])){
return false;
}
else if(charArray[2] != "."){
return false;
}
//so on and so forth.....
else {return true}
}
Like i said i can't test this, and i can almost guarantee you this code wont run. This should give you the logic involved though.
Edit: also i wrote this assuming you dont literally mean "00.00.0000" but rather "xx.xx.xxxx" x being any number 0-9. If you need to make sure it is literally zeros then just cut your string to be the first ten chars and compare it.
Use the strpos function. Something like this:
if (strpos($string,'00.00.0000 -') !== true) {
//some code
}

Check if a string contains a number, while allowing multiple decimal points?

This is to test version numbers. They need to contain only numbers, but can have multiple decimal points.
For example:
$a = '1.2.3';
is_numeric($a) returns false, and floatval($a) strips out the extra decimal sections, without returning a useful answer to the test.
Is there any PHP function that would do this?
You can use preg_match():
if(preg_match('~^([0-9]+(\.[0-9]+)*)$~', $string)) {
echo 'ok';
}
The regex pattern matches from the begin ^ to the end $ of the string and checks if it begins with a number and then contains only numbers - optionally separated by single dots - and ends with a number again.
You can use the strcspn function:
$q='1.2.3';
if (strcspn($q, '0123456789') != strlen($q))
echo "true";
else
echo "false";

How to return false if all numerical values of 0-9 are matching WITH other characters in string?

I am specifically targeting numerical only, So if I am using a phone mask using javascript on front end that filters user input to (000)000-000, basically [2-9] and [0-9] as mask (jquery.maskedinput-1.3.js) and mobile filter...
jQuery(function ($e) {
var isMobile = navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry)/);
$e('#refer').val(window.location.href);
if (!(isMobile)) {
$e('#phone').mask('(299)299-9999');
$e('#field_phone_number').mask('299-299-9999');
}
});
For server side I have a regular expression in PHP as (nothing special yet)
function phonenumber($value)
{
return preg_match("/\(?\b[(. ]?[0-9]{3}\)?[). ]?[0-9]{3}[-. ]?[0-9]{4}\b/i", $value);
}
How can a create a regex or php script that targets all numerical values without creating a very long regex for each character? I just want to know if someone types in (222)222-2222, they get a false on the return.
function phonenumber($value)
{
$prefix = '\d{3}'; // You might want to specify '2\d\d' (200 to 299)
$regex = '#^(\('.$prefix.'\)|'.$prefix.')[\s\.-]?\d{3}[\.-]?\d{4}$#';
if (preg_match($regex, $value))
{
// Number is in a suitable format
// Now extract digits -- remove this section to not test repeated pattern
$digits = preg_replace('#[^\d]+#', '', $value);
// All numbers equal are rejected
if (preg_match('#^(\d)\1{9}$#', $digits))
return false;
// end of pattern check
// Otherwise it is accepted
return true;
}
return false; // Not in a recognized format
}
This will accept (299)423-1234 and 277-111-2222, and also (400)1234567 or 4001234567. It will reject (400-1234567 and 400-12-34-56-7. It will also reject (222)222-2222 because of the repeated 2's.
You can use a backreference \1 to detect recurring patterns. In your case you can simply mix in a .* to ignore in-between fillers like ( and -
/(\d)(.*\1){7}/
Will look for a number, and at least 7 repetitions of the same, ignoring any other characters used as filler. This will not ensure that they are consecutive however, so (222)222-8222 would match too.

Validating special form of IP address

I have encountered a strange IP which has redundant zero values among the octets. Is there anyway to properly validate this as an IP or use regular expression to remove those redundant zeroes?
example is of follows:
218.064.215.239 (take note of the extra zero at the second octet "064").
I do have one working IP validation function but it will not validiate this Ip properly due to the nature of the regular expression unable to accept that extra zero. Following is the code in PHP:
function valid_ip($ip) {
return preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])" .
"(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $ip);
}
thanks for any help in advance peeps! :D
This will correct them:
$ip = "123.456.007.89";
$octets = explode(".", $ip);
$corrected = array();
foreach ($octets as $octet) {
array_push($corrected, (int)$octet);
}
echo implode(".", $corrected);
You have to accept the zero like this:
^(0?[1-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.(0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$
Play with this regular expression on rubular.com.
The 0? I added matches zero or one occurence of 0. So 0?[1-9][0-9] for example matches both 010 and 10 for example.
Change the bare |1 occurrences to |[01]. Are you sure this is not supposed to be interpreted as an octal number, though? Some resolvers do that.
Use ip2long().
You should figure out where those extra zeroes are coming from. Those leading zeroes can't be just dropped. On most platforms they mean that the octet is in octal form instead of decimal. That is: 064 octal equals 52 decimal.
Did you have a go yourself? It's really quite simple.
|1[0-9][0-9]| matches 100-199, as you are now wanting to match 000-199 (as above that it is 200-155) you just need to make a set for the 1 to be 1 or 0.
function valid_ip($ip) {
return preg_match("/^([1-9]|[1-9][0-9]|[01][0-9][0-9]|2[0-4][0-9]|25[0-5])".
"(\.([0-9]|[1-9][0-9]|[01][0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $ip);
}
And that can be refactored down (allowing leading zeroes) to:
function valid_ip($ip) {
return preg_match("/^([01]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])".
"(\.([01]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){3}$/", $ip);
}
Or to strip these unneeded zeros:
function strip_ip($ip) {
return preg_replace( '/0+([^0])(\.|$)/' , '$1$2' , $ip );
}

Categories