floatval causes the digits after the thousand sep ',' to be erased - php

floatval('19500.00');
returns 19500 ;
however
echo floatval('19,500.00');
returns 19 ;
this could've really given me a big problem it was good that I've noticed :D ... is there some reason for that behavior or it's just a bug ... should all values be number_formatted before ouput?

You put that value in single quotes, so it's not treated as a numerical value, but as a string.
Here's php.net's explanation what happens to strings with floatval (from http://php.net/manual/en/function.floatval.php):
Strings will most likely return 0 although this depends on the
leftmost characters of the string.
Meaning: The leftmost characters of the string in your case is 19 - that's a numerical value again, so the output is 19.

Decimal and thousands separator symbols are defined by current locale used in your script.
You can set default_locale in php.ini globally, or change locale in your script on the go: http://php.net/manual/en/function.setlocale.php
Different locales have different separators. To check, which is the current separator symbol, you can use localeconv function: http://us2.php.net/manual/en/function.localeconv.php
$test = localeconv();
echo $test['decimal_point'];
#.
echo $test['thousands_sep'];
#,
But actually no one can make this function work properly with all these commas and dots, so the only solution is to clean the input removing everything except "." and numbers by regexp or str_replace:
echo floatval(preg_replace("/[^0-9\.]/", "", '19,500.00'));
#19500
echo floatval(str_replace(",", "", '19,500.00'));
#19500

Related

Is there a way to delimit "ucwords()" in PHP such that the first char is not automatically uppercased?

PHP has the function ucwords(), which allows for custom delimiters. This works well, and will turn my test string into My Test String no problem.
Take the following example: I want to make a super awesome 2009 gamer tag.
$gamerTag = 'xxx_l33t_xxx'; // Not yet epic.
echo ucwords($gamerTag,"x"); // want it to return 'xXx_l33t_xXx'
I would have assumed strings would delimit case-sensitively and update the the second x in each case, ignoring the third, since at that point the middle one would no longer match our delimiter.
However, this actually returns XxX_l33t_xXx, since it will automatically uppercase the first letter in the string.
I know that there are other methods of doing this (strsplit() array loops and pregreplace with a reverse lookup come to mind), but my primary question becomes the following:
Is there a way to delimit ucwords() such that it does not automatically uppercase the first character of the string?
The internal behaviour is unfortunately that the first character of the string will always be converted to upper case, regardless of the delimiters you pass in.
Digging into the PHP source, this is the implementation of ucwords:
*r = toupper((unsigned char) *r);
for (r_end = r + Z_STRLEN_P(return_value) - 1; r < r_end; ) {
if (mask[(unsigned char)*r++]) {
*r = toupper((unsigned char) *r);
}
}
From https://github.com/php/php-src/blob/master/ext/standard/string.c#L2651
Here r is the return value, and mask is a char array of the delimiting characters. The first call to toupper (outside the of the loop) means that there's no way to prevent the first character being converted.
Because this is done, it means the second character is not converted, since it's now preceded by X, not x. The third character is handled "correctly".
This can actually cause some strange cascading behaviour, since the return value is being iterated over while it's being modified:
php > echo ucwords('aaa', 'A');
AAA
The initial string doesn't contain the delimiting character anywhere, but the result is completely upper-case.
As mentioned in a comment, there's an open PHP bug to reflect this behaviour in the documentation here: https://bugs.php.net/bug.php?id=78393

Selecting thousands separator character with RegEx

I need to change the decimal separator in a given string that has numbers in it.
What RegEx code can ONLY select the thousands separator character in the string?
It need to only select, when there is number around it. For example only when 123,456 I need to select and replace ,
I'm converting English numbers into Persian (e.g: Hello 123 becomes Hello ۱۲۳). Now I need to replace the decimal separator with Persian version too. But I don't know how I can select it with regex. e.g. Hello 121,534 most become Hello ۱۲۱/۵۳۴
The character that needs to be replaced is , with /
Use a regular expression with lookarounds.
$new_string = preg_replace('/(?<=\d),(?=\d)/', '/', $string);
DEMO
(?<=\d) means there has to be a digit before the comma, (?=\d) means there has to be a digit after it. But since these are lookarounds, they're not included in the match, so they don't get replaced.
According to your question, the main problem you face is to convert the English number into the Persian.
In PHP there is a library available that can format and parse numbers according to the locale, you can find it in the class NumberFormatter which makes use of the Unicode Common Locale Data Repository (CLDR) to handle - in the end - all languages known to the world.
So converting a number 123,456 from en_UK (or en_US) to fa_IR is shown in this little example:
$string = '123,456';
$float = (new NumberFormatter('en_UK', NumberFormatter::DECIMAL))->parse($string);
var_dump(
(new NumberFormatter('fa_IR', NumberFormatter::DECIMAL))->format($float)
);
Output:
string(14) "۱۲۳٬۴۵۶"
(play with it on 3v4l.org)
Now this shows (somehow) how to convert the number. I'm not so firm with Persian, so please excuse if I used the wrong locale here. There might be options as well to tell which character to use for grouping, but for the moment for the example, it's just to show that conversion of the numbers is taken care of by existing libraries. You don't need to re-invent this, which is even a sort of miss-wording, this isn't anything a single person could do, or at least it would be sort of insane to do this alone.
So after clarifying on how to convert these numbers, question remains on how to do that on the whole text. Well, why not locate all the potential places looking for and then try to parse the match and if successful (and only if successful) convert it to the different locale.
Luckily the NumberFormatter::parse() method returns false if parsing did fail (there is even more error reporting in case you're interested in more details) so this is workable.
For regular expression matching it only needs a pattern which matches a number (largest match wins) and the replacement can be done by callback. In the following example the translation is done verbose so the actual parsing and formatting is more visible:
# some text
$buffer = <<<TEXT
it need to only select , when there is number around it. for example only
when 123,456 i need to select and replace "," I'm converting English
numbers into Persian (e.g: "Hello 123" becomes "Hello ۱۲۳"). now I need to
replace the Decimal separator with Persian version too. but I don't know how
I can select it with regex. e.g: "Hello 121,534" most become
"Hello ۱۲۱/۵۳۴" The character that needs to be replaced is , with /
TEXT;
# prepare formatters
$inFormat = new NumberFormatter('en_UK', NumberFormatter::DECIMAL);
$outFormat = new NumberFormatter('fa_IR', NumberFormatter::DECIMAL);
$bufferWithFarsiNumbers = preg_replace_callback(
'(\b[1-9]\d{0,2}(?:[ ,.]\d{3})*\b)u',
function (array $matches) use ($inFormat, $outFormat) {
[$number] = $matches;
$result = $inFormat->parse($number);
if (false === $result) {
return $number;
}
return sprintf("< %s (%.4f) = %s >", $number, $result, $outFormat->format($result));
},
$buffer
);
echo $bufferWithFarsiNumbers;
Output:
it need to only select , when there is number around it. for example only
when < 123,456 (123456.0000) = ۱۲۳٬۴۵۶ > i need to select and replace "," I'm converting English
numbers into Persian (e.g: "Hello < 123 (123.0000) = ۱۲۳ >" becomes "Hello ۱۲۳"). now I need to
replace the Decimal separator with Persian version too. but I don't know how
I can select it with regex. e.g: "Hello < 121,534 (121534.0000) = ۱۲۱٬۵۳۴ >" most become
"Hello ۱۲۱/۵۳۴" The character that needs to be replaced is , with /
Here the magic is just two bring the string parts into action with the number conversion by making use of preg_replace_callback with a regular expression pattern which should match the needs in your question but is relatively easy to refine as you define the whole number part and false positives are filtered thanks to the NumberFormatter class:
pattern for Unicode UTF-8 strings
|
(\b[1-9]\d{0,2}(?:[ ,.]\d{3})*\b)u
| | |
| grouping character |
| |
word boundary -----------------+
(play with it on regex101.com)
Edit:
To only match the same grouping character over multiple thousand blocks, a named reference can be created and referenced back to it for the repetition:
(\b[1-9]\d{0,2}(?:(?<grouping_char>[ ,.])\d{3}(?:(?&grouping_char)\d{3})*)?\b)u
(now this get's less easy to read, get it deciphered and play with it on regex101.com)
To finalize the answer, only the return clause needs to be condensed to return $outFormat->format($result); and the $outFormat NumberFormatter might need some more configuration but as it is available in the closure, this can be done when it is created.
(play with it on 3v4l.org)
I hope this is helpful and opens up a broader picture to not look for solutions only because hitting a wall (and only there). Regex alone most often is not the answer. I'm pretty sure there are regex-freaks which can give you a one-liner which is pretty stable, but the context of using it will not be very stable. However not saying there is only one answer. Instead bringing together different levels of doings (divide and conquer) allows to rely on a stable number conversion even if yet still unsure on how to regex-pattern an English number.
You can write a regex to capture numbers with thousand separator, and then aggregate the two numeric parts with the separator you want :
$text = "Hello, world, 121,534" ;
$pattern = "/([0-9]{1,3}),([0-9]{3})/" ;
$new_text = preg_replace($pattern, "$1X$2", $text); // replace comma per 'X', keep other groups intact.
echo $new_text ; // Hello, world, 121X534
In PHP you can do that using str_replace
$a="Hello 123,456";
echo str_replace(",", "X", $a);
This will return: Hello 123X456

Unexpected string length when using strlen in php

I am stuck with the issue as currently the result is quite unexpected. I am calculating a hashed keyword length and it is surely giving me an unexpected result.
echo strlen("$2a$08$MphfRBNtQMLuNro5HOtw3Ovu20cLgC0VKjt6w7zrKXfj1bv8tNnNa");
Output - 6
Let me know the reason for this and why it is outputting 6 as a result.
Codepad Link - http://codepad.org/pLARBx6F
You must use single quotes '. With the double quotes ("), due to the $ in your string, parts of it get interpreted as variables.
Generally, it's not a bad idea to get accustomed to using single quotes unless you specifically need doubles.
Look at the "variables" contained here. They would be $2a, $08, and $MphfRBNtQM......
The first two couldn't be variables as they start with a number, thus, the 6 characters. The third one indeed could be a proper variable, but since it isn't set, it's empty.
Use the below code to calculate the string length -
echo strlen('$2a$08$MphfRBNtQMLuNro5HOtw3Ovu20cLgC0VKjt6w7zrKXfj1bv8tNnNa');
You need to use single quotes, as at the third occurrence of the symbol $, a alphabet is starting after it and it get treated as a new variable. So before this third occurrence of $ only 6 character were there and you were getting string length as 6
Try following
<?php
echo strlen('$2a$08$MphfRBNtQMLuNro5HOtw3Ovu20cLgC0VKjt6w7zrKXfj1bv8tNnNa');
?>
If you change your string and remove rest of '$' signs except the first one, then this will work fine because by adding $ it gets a special meaning in PHP.

Issue with string comparison in PHP

I have two strings with seemingly the same values. One is stored as a key in an array, the other a value in another different array. I compare the two using ==, ===, and strcmp. All treat them as different strings. I do a var_dump and this is what I get.
string(17) "Valentine’s Day"
string(15) "Valentine's Day"
Does anyone have any idea why the first string would be 17 characters and the second 15?
Update: This is slightly more obvious when I pasted this out of my editor whose font made the two different apostrophe's almost indistinguishable.
The first string contains a Unicode character for the apostrophe while the second string just has a regular ASCII ' character.
The Unicode character takes up more space.
If you run the PHP ord() function on each of those characters you'll see that you get different values for each:
echo ord("’"); //226 This is just the first 2 bytes (see comments below for details from ircmaxell)
echo ord("'"); //27
As a complement to #Mark answer above which is right (the ’ is a multi-byte character, most probably UTF-8, while ' is not). You can easily convert it to ASCII (or ISO-8859-1) using iconv, per example:
echo iconv('utf-8', 'ascii//TRANSLIT', $str);
Note: Not all characters can be transformed from multi-byte to ASCII or latin1. You can use //IGNORE to have them removed from the resulting string.
’ != '
mainly. if you want this not to be an issue, you could do something like this.
if (str_replace('’', '\'', "Valentine’s Day") == "Valentine's Day") {

Making the money value with only numbers

I just posted this question link text about 5 minutes ago and I forgot to mention that the format was like this
"$2,090.99 "
I need the final value like
"209099"
Striping the final extra space and getting rid of any other punctuation in the money value with php so i can store into a mysql decimal 10,2
You can use a regular expression to replace everything that is not a digit:
$output = preg_replace('/\D/', '', $str);
\D is equivalent to [^\d] that is equivalent to [^0-9].
You might be better off using PHP 5.3's MessageFormatter, Locale and Intl classes if you'll be handling different locales and currency formats. The msgfmt_parse() method might just be what you need.

Categories