PHP regex prevent repeated string - php

Im trying to create a regular expression in PHP which is checking that a string conforms to the following rules:
Has 1 - 7 occurrences of one of the following strings (Mon,Tues,Wed,Thurs,Fri,Sat,Sun)
Strings separated by a ,
not case sensitive
No strings are repeated.
I believe I have fulfilled the first three aspects of this with the following:
/^(Mon|Tues|Wed|Thurs|Fri|Sat|Sun)(,(Mon|Tues|Wed|Thurs|Fri|Sat|Sun)){0,6}$/i
I am struggling to get to grips with preventing any repeats. Can anyone advise?

^(Mon|Tues|Wed|Thurs|Fri|Sat|Sun)(?:,(?!\1|\2)(Mon|Tues|Wed|Thurs|Fri|Sat|Sun)){0,6}$
You could use this if regex is an absolute requirement but I'd rather recommend Martijn's answer. It is much more flexible and easier to read.
Here is how i tested this in PHP:
<?php
$subject1 = "Mon,Mon";
$subject2 = "Sun,Mon,Fri,Sun";
$subject3 = "Sat";
$subject4 = "Mon,Wed,Tues,Fri,Wed";
$subject5 = "Mon,Tues";
$pattern = '/^(Mon|Tues|Wed|Thurs|Fri|Sat|Sun)(?:,(?!\1|\2)(Mon|Tues|Wed|Thurs|Fri|Sat|Sun)){0,6}$/i';
print_r(preg_match($pattern, $subject1, $matches) . " " . $subject1 . "\n");
print_r(preg_match($pattern, $subject2, $matches) . " " . $subject2 . "\n");
print_r(preg_match($pattern, $subject3, $matches) . " " . $subject3 . "\n");
print_r(preg_match($pattern, $subject4, $matches) . " " . $subject4 . "\n");
print_r(preg_match($pattern, $subject5, $matches) . " " . $subject5 . "\n");
?>
This outputs:
0 Mon,Mon
0 Sun,Mon,Fri,Sun
1 Sat
1 Mon,Wed,Tues,Fri,Wed
1 Mon,Tues

Does it have to be a regex? If not:
$daysStart = 'Mon,Tues,Wed,mon';
$days = strtolower($daysStart);
$days = explode(",", $days); // split on comma
$days = array_unique($days); // remove uniques
$days = implode(",", $days); // join on comma
// Compare new string to original:
if(strtolower($days)===strtolower($daysStart )){ /*match*/ }
This results in a lowercase string of days, seperated by commas. Not sure what you wanted as output, you might want to save the original input in another far, or ucfirst() the values via an array_map() or something, this is just to show you another method
Or my code shorter:
$daysStart = 'Mon,Tues,Wed,mon';
$days = explode(",", strtolower($daysStart ) );
$days = implode(",", array_unique($days) );
if(strtolower($days)===strtolower($daysStart )){ /*match*/ }
or function (as short code, can be the longer version ofcourse):
function checkDays($string){
$days = explode(",", strtolower($string) );
$days = implode(",", array_unique($days) );
return (strtolower($days)===strtolower($daysStart)) ? true : false;// *
}
*I could've done just the return and the str-checks, but I prefer to add true/false in a way im sure my returnvalue always is true of false as boolean, not truthy or falsy.

Related

Wrong calculations in PHP

I am trying to calculate in PHP. When I try to do that I can see that the result is mostly wrong.
The script I am using is the following:
$newrate = 1 / $rate["rate"];
echo '1 /'. $rate["rate"]. ' = '.$newrate;
The result I get is the following:
1 /1.0867 = 1
1 /117.01 = 0.0085470085470085
1 /1.9558 = 1
1 /27.534 = 0.037037037037037
1 /7.4589 = 0.14285714285714
1 /1.0867 should be 0.92021717125 and not 1.
I can reproduce this step by adding the following line to my script:
$rate["rate"] = 1.0867;
When I run the script the result is still 1. When I change my script like:
$newrate = 1 / 1.0867;
echo '1 / 1.0867 = '.$newrate;
I get a correct result. The output is then: 0.92021717125.
Does someone know what is wrong with this script? What is the reason that my script is making incorrect calculations?
Update 1:
Here is my full script:
<?php
$XML=simplexml_load_file("https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml");
foreach($XML->Cube->Cube as $rat)
{
foreach($rat as $rate)
{
$newrate = '1' / $rate["rate"];
echo '1 /' . $rate["rate"] . ' = ' . $newrate;
echo '<br /><br />';
}
}
?>
I think there is something wrong with your example. I tested exactly what you described and works as expected.
<?php
$rate = [];
$rate["rate"] = "1.0867";
$newrate = 1 / $rate["rate"];
echo '1 / ' . $rate["rate"] . ' = ' . $newrate;
// 1 / 1.0867 = 0.92021717125242
You can see it running here
https://repl.it/#thiagodamata/NeighboringPeachpuffArchitect
Probably, you are using "," instead of "." when sending the number.
PHP cast the number only until it hits something unexpected. So "1,0867" is cast to 1. It is a classic problem when dealing with numbers in different formats, considering different languages.
<?php
// simulating the "," error
$rate["rate"] = "1,0867";
$newrate = 1 / $rate["rate"];
echo '1 / ' . $rate["rate"] . ' = ' . $newrate;
// 1 / 1,0867 = 1
Take a look in this thread about how to cast the number from different languages masks PHP: Locale aware number format and take a deeper look into the PHP function number-format https://www.php.net/manual/en/function.number_format.php
Update - After getting the full code example
Looking to your full code becomes more clear the problem. The var that you are getting in the loop it is a SimpleXmlElement, not a string. So, SimpleXmlElement is printed as the "1.0867" but it is not its real value.
To make the SimpleXmlElement cast to float properly, you need to use the cast function.
<?php
$XML=simplexml_load_file("https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml");
foreach($XML->Cube->Cube as $rat)
{
foreach($rat as $rate)
{
print(var_dump($rate["rate"]));
/*
object(SimpleXMLElement)#7 (1) {
[0]=>
string(6) "1.0867"
}
*/
print("class = [" . get_class($rate["rate"]) . "]\n");
// class = [SimpleXMLElement]
print("as string = [" . $rate["rate"] . "]\n");
// as string = [1.0867]
print("without cast = [" . (1 * $rate["rate"]) . "]\n");
// without cast = [1]
$a = (float)($rate["rate"]);
print("with cast = [" . ($a) . "]\n");
// with cast = [1.0867]
print(serialize($a));
//d:1.0867;
$newrate = '1' / $a;
echo '1 / ' . $a . ' = ' . $newrate . "\n";
// 1 / 1.0867 = 0.92021717125242
echo '<br /><br />';
exit();
}
}
?>
You can see this running here:
https://repl.it/#thiagodamata/FuchsiaLightheartedPrintablecharacter
Just printing the value of a var does not always give you the real value of some object. There are other functions like var_export https://www.php.net/manual/en/function.var-export.php, var_dump https://www.php.net/manual/en/function.var-dump.php and serialize that may help you to see the real value of the vars.
Calculations are fine, and correct
You've not taken into account implicit type casting in PHP. Try this:
<?php
print "integer divided by float = " . (1 /1.0867) . "\n"; // returns 0.9202171712524
print "Integer divided by string = " . ((1/"1.0867") . "\n"; // returns 1
You are expecting PHP to know what your intention is when you throw a tring into an arithmetic operation - that's not how PHP works.
Just because it is dynamically typed doesn't mean you don't need to be careful with your data types.
print (1/(float)$rate['rate'];
try cast all values as a float
echo 1.0 / (float) $rate['rate'];

Modify decimal point and thousands separator without changing the number of decimals

I'm new to php and I'm trying to use number_format :
number_format ( float $number , int $decimals = 0 , string $dec_point = "." , string $thousands_sep = "," )
As in the title, my goal is to modify decimal point and thousands separator without changing the number of decimals as below:
$Num=123456.789;
$Num2=number_format ($Num, [decimals as in $Num], ",", "'" );
My result should be:
$Num2="123'456,789";
Edit
I need a code for an unknown number of decimals
You can use NumberFormatter.
You will still need to specify a certain amount of fraction digits, but you can just use a high enough value and be fine. It's not like the number of digits is really arbitrary. It's tied to your precision ini setting.
$formatter = new NumberFormatter("en_US", NumberFormatter::DECIMAL);
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 42);
$formatter->setSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL, "'");
$formatter->setSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL, ",");
echo $formatter->format(123456.7891234); // 123'456,7891234
Demo https://3v4l.org/TCAIA
You can do it such a way (firstly take a look to #Gordon answer – it's much more better):
<?php
function extendedNumberFormat($num, $decimalSeparator, $thousandSeparator) {
$asStr = strval($num);
$exploded = explode('.', $asStr);
$int = $exploded[0];
$decimal = isset($exploded[1]) ? $exploded[1] : null;
$result = number_format($int, 0, ".", $thousandSeparator);
if ($decimal !== null) {
$result .= $decimalSeparator . $decimal;
}
return $result;
}
echo extendedNumberFormat(123456.789, ',', "'") . "\n";
echo extendedNumberFormat(123456.7891, ',', "'") . "\n";
echo extendedNumberFormat(123456, ',', "'") . "\n";
//123'456,789
//123'456,7891
//123'456

Get range letter and number with php

how can I get range for bottom string in php?
M0000001:M0000100
I want result
M0000001
M0000002
M0000003
..
..
..
M0000100
this is what i do
<?php
$string = "M0000001:M0000100";
$explode = explode(":",$string );
$text_one = $explode[0];
$text_two = $explode[1];
$range = range($text_one,$text_two);
print_r($range);
?>
So can anyone help me with this?
This is one of many ways you could do this and this is a little verbose but hopefully it shows you some "steps" to take.
It doesn't check for the 1st number being bigger than the 2nd.
It doesn't check your Range strings start with a "M".
It doesn't have all of the required comments.
Those are things for you to consider and work out...
<?php
$string = "M00000045:M000099";
echo generate_range_from_string($string);
function generate_range_from_string($string) {
// First explode the two strings
$explode = explode(":", $string);
$text_one = $explode[0];
$text_two = $explode[1];
// Remove the Leading Alpha character
$range_one = str_replace('M', '', $text_one);
$range_two = str_replace('M', '', $text_two);
$padding_length = strlen($range_one);
// Build the output string
$output = '';
for ( $index = (int) $range_one; $index <= (int) $range_two; $index ++ ) {
$output .= 'M' . str_pad($index, $padding_length, '0', STR_PAD_LEFT) . '<br>';
}
return $output;
}
The output lists a String in the format you have specified in the question. So this is based solely upon that.
This could undergo a few more revisions to make it more function like, as I'm sure some folks will pick out!

Preg_match with different combinations of words

I've a littile question about preg_matches, these regex things are really hard to understand and I hope someone can give the right awnser!
I have the following text:
0A-24-423
But this can also be:
0A-242-2
or
0A-2-423
How can I use preg_matches to filter these? I was using
substr($something, 0,2)
so that it captures 0A and
substr($seat, 4,5)
This will capture 24 but when you get 242 it wont capture the last 2....
Hope someone can help creating this in preg_match!
to make it more clear what I have now:
foreach($_POST['seats'] AS $seat) {
if ($count > 0) {
$selectQuery .= " || ";
}
$selectQuery .= " ( rowId = '" . substr($seat, 0,2) . "'";
$selectQuery .= " and `order` = " . substr($seat, 3,5) . " ";
$selectQuery .= " and columnId = " . substr($seat, 6) . " ) ";
$count++;
and $seat had the following format XXXXXX and using substr I can get the right things (for example: 0J3017)
Something Like this should do it:
$selectQuery = "SELECT * from seats where ";
$count = 0;
$pattern = "I DON'T KNOW :( ";
foreach($_POST['seats'] AS $seat) {
if ($count > 0) {
$selectQuery .= " || ";
}
preg_match($pattern, $seats, $matches);
$selectQuery .= " ( rowId = '" . $matches[0] . "'";
$selectQuery .= " and `order` = " . $matches[1] . " ";
$selectQuery .= " and columnId = " . $matches[2] . " ) ";
$count++;
and $seats is explained in the beginning of the post (it has a format of XX-XXX-XXX
where the first 2 XX are 0[A-Z] (yes the 0 is correct)
where the 3 first XXX are [0-9]
Where the last 3 XXX are [0-9]
EDIT:
there are 2 ways to solve this.
Option 1:
$pattern = "/(.*)-(.*)-(.*)/";
or use explode() function.
It does not look like you need to be using regular expressions. Here's an example using explode() and list():
list($row_id, $order, $column_id) = explode('-', $seat, 3);
You could then use those three new variables in your $selectQuery.
EDIT : Since the OP has stated his requirement as a comment to my answer I have updated my answer accordingly.
You can try this:
$pattern = "/[A-Z\d]{1,2}-[A-Z\d]{1,3}-[A-Z\d]{1,3}/";
$matched = preg_match($pattern, $something);
if ($matched === 0) {
die('regex did not match');
}
$matched will give you 1 for a matched string and 0 if not matched.

PHP regex vs explode() for phone number

I have been messing around with some regular expressions, and I ran into a small hiccup with a more complicated version of my phone number formatting.
Here's what I am working with:
$number1 = '+1 (123) 1234567';
$number1 = '+966 (1) 1234567 x555';
These strings are actually being output from the MySQL query I created, and I love it.
However, I'm making a simple php function to auto-format the subscribers number from 1234567 to 123-4567.
I'm not too concerned about any number that doesn't start with +1. So I'm formatting US and Canadian numbers.
Here's what I attempted, if there was only 7 digits, and if the string starts with +1
<?php
function format_phonenumbers($phone){
if(empty($phone)){ return ''; }
$exploded = explode(' ',$phone);
$countrycode = $exploded[0];
$areacode = $exploded[1];
$number = $exploded[2];
$ext = (!empty($exploded[3])?$exploded[3]:'');
if($countrycode=='+1'){
$strphone = strlen($number);
if ($strphone == 7) { // auto-format US PHones
$prefix = substr($number,0,3);
$suffix = substr($number,-4);
}
$phone = $countrycode.' '.$areacode.' '.$prefix.'-'.$suffix.' '.$ext;
}
return $phone;
}
echo format_phonenumbers('+1 (714) 1234567'); // US domestic
echo '<br>';
echo format_phonenumbers('+966 (1) 1234567 x555'); // international
?>
This formats what I need, but I'm curious if anyone believes I can do this in a better way. Like using a regex checker, that finds anything after the parenthesis, but before an extension, rather than using the explode() function.
Something like:
function format_phonenumbers($phone)
{
return preg_replace_callback(
'/([)]\s*)(\d{3,3}(\d+)/',
function ($match) {
return $match[1] . $match[2] . '-' . $match[3];
},
$phone
);
}
This should work, but requires PHP 5.3.0 or higher (if not you'll have to use create_function). Its arguable if it is any better however.

Categories