PHP 'NumberFormatter' 'SPELLOUT' is not providing required format in english - php

I Am using PHP NumberFormatter in my code to convert price value in words
for example, if it is rupees 125 then it should be 'One hundred and twenty five' instead of 'one hundred twenty five'.
I have tried the other examples like checking each digit unit value and replace the words
$numberFormatterClass = new \NumberFormatter("en", \NumberFormatter::SPELLOUT);
echo str_replace('-', ' ', $numberFormatterClass->format($number));
expecting for 125 = "One hundred and twenty five"

When the number is above 99 you can generate the spellout for the last two digits only. You then know where to insert the "and". In code:
$number = 125;
$numberFormatter = new \NumberFormatter('en', \NumberFormatter::SPELLOUT);
$fullSpellout = str_replace('-', ' ', $numberFormatter->format($number));
if ($number > 100) {
$lastTwoSpellout = str_replace('-', ' ', $numberFormatter->format(substr($number, -2)));
$hunderdsLength = strlen($fullSpellout) - strlen($lastTwoSpellout);
$fullSpellout = substr($fullSpellout, 0, $hunderdsLength) . 'and ' . $lastTwoSpellout;
}
echo $fullSpellout;
This outputs:
one hundred and twenty five
This is certainly not the only possible solution. There are many ways to insert the "and", and if the last two digits always generate two words you could also use that to detect where to insert the "and".
Here's a version based on words and using an array to insert the 'and':
$number = 125;
$numberFormatter = new \NumberFormatter('en', \NumberFormatter::SPELLOUT);
$spellout = str_replace('-', ' ', $numberFormatter->format($number));
if ($number > 100) {
$words = explode(' ', $spellout);
array_splice($words, -2, 0, ['and']);
$spellout = implode(' ', $words);
}
echo $spellout;

Related

PHP convert String number with whitespace to Float

I have a class with field price. It's stored in database as varchar(100) (don't ask me why, not my idea) and I have to retrieve it and divide it by the other price, which is also retrieved from database.
In database number looks like: 1 000.00 - thousands' parts are separated with whitespace (one million looks like ->1 000 000.00).
I try it on website: w3resource.com/php with an online interpreter.
What I do is...
$a = "3 999.99";
$b = "1 500.11";
$aa = floatval(str_replace(' ', '', $a));
$bb = floatval(str_replace(' ', '', $b));
$c = $aa - $bb;
echo $aa . ' - ' . $bb . ' = ' . $c;
and it displays correct output:
3999.99 - 1500.11 = 2499.88
But when I do it in my project, it's like:
$a = "3 999.99";
$b = "1 500.11";
$aa = floatval(str_replace(' ', '', $a));
$bb = floatval(str_replace(' ', '', $b));
$c = $aa - $bb;
echo $aa . ' - ' . $bb . ' = ' . $c;
3999.99 - 1500.11 = 2.00
In my project, price is truncated - everything that is after the whitespace is truncated and the calculations are performed only on the first digits, in my case it's: 3 - 1
Could anybody tell me where to look for a bug?
Most likely you have a non visible character as the thousand separator character. It's more safe to filter all characters except digits and . when sanitizing the input string. This can be achieved with the preg_replace function.
Your script can be modified to something like this:
$a = "3 999.99";
$b = "1 500.11";
$aa = floatval(preg_replace('/[^0-9.]/', '', $a));
$bb = floatval(preg_replace('/[^0-9.]/', '', $b));
$c = $aa - $bb;
echo $aa . ' - ' . $bb . ' = ' . $c;
Additionally, it's a bit dangerous and non exact to use floats when handling money in particular. Please have a look at this repository which helps a lot with this problem: https://github.com/moneyphp/money
The biggest mistake is price as varchar(100) - it's a string, it can be displayed in a million ways, but it cannot be recalculated. There are no excuses. It should be an integer or decimal and any subtraction should be done using SQL.
The second error is when you use a float as the value for price. Leave php float for astrophysicists, use int.
$a = "3 999.99";
$b = "1 500.11";
$a1 = (int) preg_replace('/[^0-9]/', '', $a);
$b1 = (int) preg_replace('/[^0-9]/', '', $b);
var_dump($a1, $b1); // test
echo'<hr>';
$c1 = $a1 - $b1;
var_dump($a1, $b1, $c1); // test
formatting to display:
echo substr_replace(substr_replace($c1,' ',-5,0), '.',-2,0);

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

VARCHAR SQL contains numbers how to get 2 decimals after comma

I'm having some trouble getting the price to show correct on my website. Currently i have a row VerkoopPP40 which is a VARCHAR input. In this row there is a price e.g. 89,5 or just 9. When I try to get these values it does some unexpected things.
**** Update ****
I've just tried this code:
<?php
function formatNumber($number, $format=[], $oldDecimalSeparator=",.·'", $multiplier=1)
{
if ($format) {
$format += ['numOfDecimals' => 0, 'decimalSeparator' => '.', 'thousandSeparator' => '']; # Default format
# Find decimal separator
# The decimal separator is the one that is the last and does not occur more than once
if ($letters = str_replace(' ', '', $number)) { # Replace spaces
if ($letters = preg_replace('/^-/', '', $letters)) { # Remove minus
if ($letters = preg_replace('/[0-9]/', '', $letters)) { # Get all non digits
$lastletter = substr($letters, -1); # Returns last char
if (substr_count($letters, $lastletter) == 1) {
if (strpos($oldDecimalSeparator, $lastletter) !== false)
$oldDecimalSep = $lastletter;
else
return $number;
}
}
}
}
$number = preg_replace('/[^0-9-]/', '', $number); # Remove all non digits except 'minus'
if ($oldDecimalSep)
$number = str_replace($oldDecimalSep, '.', $number); # Format to float
if ($multiplier != 1)
$number = $number * $multiplier;
# Convert float to new format
$number = number_format($number,
$format['numOfDecimals'],
$format['decimalSeparator'],
$format['thousandSeparator']
);
}
return $number;
}
This returns: 9,00 and 895,00 so the comma is in a different place right now. It's something I guess... Anyone got an idea to move the comma and remove a 0.
**** End Update ****
And echo-ed it like this:
<td><p>vanaf " . formatNumber($number, [
'numOfDecimals' => 2,
'decimalSeparator' => ',',
'thousandSeparator' => ' '
], ',.') . " p.p. <small>Excl btw</small></p></td>
If I just echo the VerkoopPP40 row it returns: €89,5 or €9.
So I googled around some and found this:
$var = $row["VerkoopPP40"];
$var = ltrim($var, '0');
$foo = $var;
$prijzen = number_format($foo, 2, ',', '');
This turns the . into a ,. But also returns €9,00 for the row that has 9 in it. But the strange thing is the row that has 89.5 in it now just returns €89,00. So somewhere in the code it rounds the numbers down.
Does anyone know how to get the price to show just €9,00 and €89,50 respectively.
I tried the following codes as well:
SELECT ROUND(VerkoopPP40,2) AS RoundedPrice
As database query. That didn't work.
$prijzen = CAST($prijzen as decimal(2,2));
Also didn't work. Any more ideas?
Don't know if this will help you, but found in the comments of the PHP doc : "To prevent the rounding that occurs when next digit after last significant decimal is 5 (mentioned by several people)..." read more
$num1 = "89,5";
$num2 = str_replace(',', '.', $num1);
$price = number_format($num2, 2, '.', '');
echo"[ $price ]";
you should use number_format but in the right way let me explain it to you
you tried this with 89.5
$prijzen = number_format($foo, 2, ',', '');
but this is written for 89,5 not for 89.5
//this will work for you
$var = $row["VerkoopPP40"];
echo 'raw output from database is :'.$var;
$var = $var / 10;
echo 'after this step the number is :'.$var;
$var = number_format($var, 2, '.', '');
echo 'after this step the number is :'.$var;
number_format(the input number, decimal places, 'the divider between whole numbers and decimals', '')

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!

Better / Cleaner method for splitting a string based on number of words?

I was in need of a method to count the number of words (not characters) within PHP, and start a <SPAN> tag within HTML to wrap around the remaining words after the specified number.
I looked into functions such as wordwrap and str_word_count, but those didn't seem to help. I went ahead and modified the code found here: http://php.timesoft.cc/manual/en/function.str-word-count.php#55818
Everything seems to work great, however I wanted to post here as this code is from 2005 and maybe there is a more modern / efficient way of handling what I'm trying to achieve?
<?php
$string = "One two three four five six seven eight nine ten.";
// the first number words to extract
$n = 3;
// extract the words
$words = explode(" ", $string);
// chop the words array down to the first n elements
$first = array_slice($words, 0, $n);
// chop the words array down to the retmaining elements
$last = array_slice($words, $n);
// glue the 3 elements back into a spaced sentence
$firstString = implode(" ", $first);
// glue the remaining elements back into a spaced sentence
$lastString = implode(" ", $last);
// display it
echo $firstString;
echo '<span style="font-weight:bold;"> '.$lastString.'</span>';
?>
You could use preg_split() with a regex instead. This is the modified version of this answer with an improved regex that uses a positive lookbehind:
function get_snippet($str, $wordCount) {
$arr = preg_split(
'/(?<=\w)\b/',
$str,
$wordCount*2+1,
PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
);
$first = implode('', array_slice($arr, 0, $wordCount));
$last = implode('', array_slice($arr, $wordCount));
return $first.'<span style="font-weight:bold;">'.$last.'</span>';
}
Usage:
$string = "One two three four five six seven eight nine ten.";
echo get_snippet($string, 3);
Output:
One two three four five six seven eight nine ten.
Demo
Lets more even simple . Try this
<?php
$string = "One two three four five six seven eight nine ten.";
// the first number words to extract
$n = 2;
// extract the words
$words = explode(" ", $string);
for($i=0; $i<=($n-1); $i++) {
$firstString[] = $words[$i]; // This will return one, two
}
for($i =$n; $i<count($words); $i++) {
$firstString[] = $words[$i]; // This will return three four five six seven eight nine ten
}
print_r($firstString);
print_r($firstString);
?>
Demo here
I borrowed the code from here:
https://stackoverflow.com/a/18589825/1578471
/**
* Find the position of the Xth occurrence of a substring in a string
* #param $haystack
* #param $needle
* #param $number integer > 0
* #return int
*/
function strposX($haystack, $needle, $number){
if($number == '1'){
return strpos($haystack, $needle);
}elseif($number > '1'){
return strpos($haystack, $needle, strposX($haystack, $needle, $number - 1) + strlen($needle));
}else{
return error_log('Error: Value for parameter $number is out of range');
}
}
$string = "One two three four five six seven eight nine ten.";
$afterThreeWords = strposX($string, " ", 3);
echo substr($string, 0, $afterThreeWords); // first three words
This looks good to me, here's another way that you might check against this for efficiency?
I have no idea which is quicker. My guess is yours is quicker for longer strings
$string = "This is some reasonably lengthed string";
$n = 3;
$pos = 0
for( $i = 0; $i< $n; $i++ ){
$pos = strpos($string, ' ', $pos + 1);
if( !$pos ){
break;
}
}
if( $pos ){
$firstString = substr($string, 0, $pos);
$lastString = substr($string, $pos + 1);
}else{
$firstString = $string;
$lastString = null;
}

Categories