regular expressions to replace money pattern - php

I would like to ask for help with this, I wanted to check for a string
matching a decimal followed by a letter (15M, 15.3M, 15T or 15.3T) and
replace it with zeroes.
Like these:
15M -> 15000000
15.3M -> 15300000
15T -> 15000
15.3T -> 15300
I tried doing this with str_replace but can't get it right. Hope
someone can help me.

"T" could be "thousand" or "trillion", you know.
$s = "15.7T";
$factors = Array('T' => 1000, 'M' => 1000000, 'B' => 1000000000.);
$matches = Array();
if (0 < preg_match('/([0-9]+(?:\.[0-9]*)?)([TMB])/', $s, $matches)) {
print("$s -> " . $matches[1] * $factors[$matches[2]]);
}
prints:
15.7T -> 15700
edit:
Anchoring (makes any garbage on the front or back mean no match):
'/^(...)$/'
You may want to make whitespace allowed, however:
'/^\s*(...)\s*$/'
You can also use "\d" in place of "[0-9]:
'/(\d+(?:\.\d*)?)([TMB])/'
Case insensitivity:
'/.../i'
...
print("$s -> " . $matches[1] * $factors[strtoupper($matches[2])]);
Optional factor:
'/([0-9]+(?:\.[0-9]*)?)([TMB])?/'
$value = $matches[1];
$factor = isset($matches[2]) ? $factors[$matches[2]] : 1;
$output = $value * $factor;
Use printf to control output formatting:
print($value) -> 1.57E+10
printf("%f", $value); -> 15700000000.000000
printf("%.0f", $value); -> 15700000000
Stephan202 recommended a clever trick, put the "?" (optional) within the parens and you're guaranteed a match string, it just might be blank. Then you can use the blank as an array key to get the default value without having to test whether it matched or not.
Putting all that together:
$factors = Array('' => 1, 'T' => 1e3, 'M' => 1e6, 'B' => 1e9);
if (0 < preg_match('/^\s*(\d+(?:\.\d*)?)([TMB]?)\s*$/i', $s, $matches)) {
$value = $matches[1];
$factor = $factors[$matches[2]];
printf("'%s' -> %.0f", $s, $value * $factor);
}

Alternative version which performs a conversion on all prices found:
$multiply = array('' => 1, 'T' => 1000, 'M' => 1000000);
function convert($matches) {
global $multiply;
return $matches[1] * $multiply[$matches[3]];
}
$text = '15M 15.3M 15.3T 15';
print(preg_replace_callback('/(\d+(\.\d+)?)(\w?)/', 'convert', $text));

In Perl:
#!/usr/bin/perl
use strict;
use warnings;
my %factors = (
B => 1_000_000_000,
M => 1_000_000,
T => 1_000,
);
while ( <DATA> ) {
next unless m{
^
(?<number> -? [0-9]+ (?:\.[0-9]+)? )
(?<factor>B|M|T)?
$
}x;
my $number = $+{factor}
? sprintf( '%.0f', $+{number} * $factors{ $+{factor} } )
: $+{number}
;
print $number, "\n";
}
__DATA__
15M
15B
15T
15.3M
15.3B
15.3T
15
15.3
Output:
C:\Temp> z
15000000
15000000000
15000
15300000
15300000000
15300
15
15.3

Have no idea how to make it in one regular expression, here is my try in Ruby.
#!/usr/bin/ruby
def trans s
h={"M"=>10**6 ,"T"=>10**3}
a = s.split(/[MT]/) + [s.split("").last]
(a[0].to_f * h[a[1]]).to_i
end
puts trans "15M"
puts trans "15.3M"
puts trans "15T"
puts trans "15.3T"

Related

PHP remove first zero from decimals without round off

How to remove the first zero in a decimal number without round off.
whenever a function detect the first zero in decimal stop it and remove the zero and excess decimals.
I tried this:
$number = 1.063;
echo floor_dec($number,$deg);
function floor_dec($number, $deg = null)
{
if ($deg == null)
return $number * 1000 / 1000;
else
return $number * pow(10, $deg) / pow(10, $deg);
}
Desired output:
1.063 -> output 1
1.30720-> output 1.3
1.3072-> output 1.3
1.823070 -> output 1.823
Tricky one using strpos()
function removeFirstZeroToEnd($number)
{
$str = explode('.',$number);
if(isset($str[1])){
// find first zero after decimal
$num = substr($str[1],0, strpos($str[1], "0"));
// adding zero will convert it to number
$number = ($str[0].'.'.$num) + 0;
}
return $number;
}
Seems like this would be a lot easier by just treating it as a string and then just get the desired substring and then convert back to float:
floatval(rtrim(substr($number, 0, strpos($number, 0)), "."));
Obligatory regex solution:
$numbers = [
123,
1.063,
1.30720,
1.3072,
1.823070
];
foreach ($numbers as &$n) {
$n = preg_replace ('~^([0-9]+(\.[1-9]+)?).*~','$1',$n);
}
print_r($numbers);
Output:
Array
(
[0] => 123
[1] => 1
[2] => 1.3
[3] => 1.3
[4] => 1.823
)

Split string at column positions

I am using
ps -l -u user
to get the running processes of a given user.
Now, when I want to split the information into arrays in PHP I am in trouble because ps outputs the data for humans to read without fixed delimiters. So you can't split with space or tab as regex.
So far I can only detect the columns by character positions.
Is there any way in php to split a string into an array at certain positions? Something like:
$array=split_columns($string, $positions=array(1, 10, 14))
to cut a string into pieces at positions 1, 10 and 14?
I decided to try a regex approach with dynamic pattern building. Not sure it is the best way, but you can give it a try:
function split_columns ($string, $indices) {
$pat = "";
foreach ($indices as $key => $id) {
if ($key==0) {
$pat .= "(.{" . $id . "})";
} else if ($key<count($indices)) {
$pat .= "(.{" . ($id-$indices[$key-1]) . "})";
}
}
$pats = '~^'.$pat.'(.*)$~m';
preg_match_all($pats, $string, $arr);
return array_slice($arr, 1);
}
$string = "11234567891234567\n11234567891234567"; // 1: '1', 2: '123456789', 3: '1234', 4: '567'
print_r (split_columns($string, $positions=array(1, 10, 14)));
See the PHP demo
The point is:
Build the pattern dynamically, by checkign the indices, subtracting the previous index value from each subsequent one, and append the (.*)$ at the end to match the rest of the line.
The m modifier is necessary for ^ to match the start of the line and $ the end of the line.
The array_slice($arr, 1); will remove the full match from the resulting array.
A sample regex (meeting OP requirements)) will look like ^(.{1})(.{9})(.{4})(.*)$
I modified Wiktor's solution as I don't need that many information.
function split_columns ($string, $indices) {
$pat = "";
foreach ($indices as $key => $id) {
if ($key==0) {
$pat .= "(.{" . $id . "})";
} else if ($key<count($indices)) {
$pat .= "(.{" . ($id-$indices[$key-1]) . "})";
}
}
$pats = '~^'.$pat.'(.*)$~m';
preg_match_all($pats, $string, $arr, PREG_SET_ORDER);
$arr=$arr[0];
return array_slice($arr, 1);
}
In PHP preg_split will help you here. You can split by a number of whitespaces e.g.:
<?
$text = '501 309 1 4004 0 4 0 2480080 10092 - S 0 ?? 0:36.77 /usr/sbin/cfpref
501 310 1 40004004 0 37 0 2498132 33588 - S 0 ?? 0:23.86 /usr/libexec/Use
501 312 1 4004 0 37 0 2471032 8008 - S 0 ?? 19:06.48 /usr/sbin/distno';
$split = preg_split ( '/\s+/', $text);
print_r($split);
If you know the number of columns you can then go through the array and take that number of columns as one row.

Trim symbol and decimal value from currency string

I have currency input strings like these.
$50 ...I only need 50
$60.59 ...I only need 60, need to remove $ and .59
€360 ...I only need 360
€36.99 ...I only need 36, need to remove € and .99
£900 ...I only need 900
£90.99 ...I only need 90
In other words, I need to remove all currency symbols from the start of the string and I only need the integer value -- the decimal values can be cut off.
This RegEx should do it
(\$|€|£)\d+
This is even better (thanks to Jan)
[$€£]\d+
Use it with PHP's Preg Match
preg_match — Perform a regular expression match
I would recommend not using a regular expression, as it's overkill for this scenario.
$str = (int)ltrim($str, '$£€');
this is all you need.
Performance vs Regex
I ran the above test through a script to see what the time difference is between my answer and using a RegEx, and on average the RegEx solution was ~20% slower.
<?php
function funcA($a) {
echo (int)ltrim($a, '$£€');
};
function funcB($a) {
echo preg_replace('/^.*?([0-9]+).*$/', '$1', $a);
};
//setup (only run once):
function changeDataA() {}
function changeDataB() {}
$loops = 50000;
$timeA = 0.0;
$timeB = 0.0;
$prefix = str_split('€$€');
ob_start();
for($i=0; $i<$loops; ++$i) {
$a = $prefix[rand(0,2)] . rand(1,999) . '.' . rand(10,99);
$start = microtime(1);
funcA($a);
$timeA += microtime(1) - $start;
$start = microtime(1);
funcB($a);
$timeB += microtime(1) - $start;
}
ob_end_clean();
$timeA = round(1000000 * ($timeA / $loops), 3);
$timeB = round(1000000 * ($timeB / $loops), 3);
echo "
TimeA averaged $timeA microseconds
TimeB averaged $timeB microseconds
";
Timings vary depending on system load, so times should be considered only relative to each other, not compared between executions. Also this isn't a perfect script for performance benchmarking, there are outside influences that can affect these results, but this gives a general idea.
TimeA averaged 5.976 microseconds
TimeB averaged 6.831 microseconds
Use regular expression. Ex:
$toStr = preg_replace('/^.*?([0-9]+).*$/', '$1', $fromStr);
See preg_replace documentation.
use below way
$str = '$50 From here i need only 50
$60.59 From here i need only 60, Need to remove $ and .59
€360 From here i need only 360.
€36.99 From here i need only 36 need to remove € and .99.
£900 From here i need only 900.
£90.99 From here i need only 90.';
$arr_ = array('$','€','£');
echo str_replace($arr_,'',$str);
You could go for:
<?php
$string = <<<DATA
$50 From here i need only 50
$60.59 From here i need only 60, Need to remove $ and .59
€360 From here i need only 360.
€36.99 From here i need only 36 need to remove € and .99.
£900 From here i need only 900.
£90.99 From here i need only 90.
DATA;
# look for one of $,€,£ followed by digits
$regex = '~[$€£]\K\d+~';
preg_match_all($regex, $string, $amounts);
print_r($amounts);
/*
Array
(
[0] => Array
(
[0] => 50
[1] => 60
[2] => 360
[3] => 36
[4] => 900
[5] => 90
)
)
*/
?>
See a demo on ideone.com.
$newString=$string;
$currencyArray = array("$","€","£"); //just add the new item if you want that to add more
foreach($currencyArray as $value)
$newString= str_replace($value,"",$newString);
$newString has what you need.

PHP equivalent to JavaScript's string split method

I'm working with this on JavaScript:
<script type="text/javascript">
var sURL = "http://itunes.apple.com/us/app/accenture-application-for/id415321306?uo=2&mt=8&uo=2";
splitURL = sURL.split('/');
var appID = splitURL[splitURL.length - 1].match(/[0-9]*[0-9]/)[0];
document.write('<br /><strong>Link Lookup:</strong> <a href="http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/wa/wsLookup?id=' + appID + '&country=es" >Lookup</a><br />');
</script>
This script takes the numeric ID and gives me 415321306.
So my question is how can I do the same thing but using PHP.
Best regards.
Use PHP's explode() function instead of .split().
splitURL = sURL.split('/'); //JavaScript
becomes
$splitURL = explode('/', $sURL); //PHP
An use preg_match() instead of .match().
$appID = preg_match("[0-9]*[0-9]", $splitURL);
I'm a little unclear on what you're doing with the length of the string, but you can get substrings in php with substr().
Who needs regex?
<?php
$sURL = "http://itunes.apple.com/us/app/accenture-application-for/id415321306?uo=2&mt=8&uo=2";
$appID = str_replace('id','',basename(parse_url($sURL, PHP_URL_PATH)));
echo $appID; // output: 415321306
?>
preg_match("/([0-9]+)/",$url,$matches);
print_r($matches);
The two functions you desire are:
http://www.php.net/manual/en/function.preg-split.php
http://www.php.net/manual/en/function.preg-match.php
Javascript split can also be used to convert a string into a character array (empty argument) and the first argument can be a RegExp.
/*
Example 1
This can be done with php function str_split();
*/
var str = "Hello World!"
str.split('');
H,e,l,l,o, ,W,o,r,l,d,!
/*
Example 1
This can be done with php function preg_split();
*/
var str = " \u00a0\n\r\t\f\u000b\u200b";
str.split('');
, , , , ,,,​
From Ecma-262
Returns an Array object into which substrings of the result
of converting this object to a String have been stored. The
substrings are determined by searching from left to right for
occurrences of separator; these occurrences are not part of any
substring in the returned array, but serve to divide up the String
value. The value of separator may be a String of any length or it
may be a RegExp object (i.e., an object whose [[Class]] internal
property is "RegExp"; see 15.10). The value of separator may be an
empty String, an empty regular expression, or a regular expression
that can match an empty String. In this case, separator does not
match the empty substring at the beginning or end of the input
String, nor does it match the empty substring at the end of
the previous separator match. (For example, if separator is the
empty String, the String is split up into individual characters; the
length of the result array equals the length of the String,
and each substring contains one character.) If separator is a
regular expression, only the first match at a given position of the
this String is considered, even if backtracking could yield a
non-empty-substring match at that position. (For example,
"ab".split(/a*?/) evaluates to the array ["a","b"], while
"ab".split(/a*/) evaluates to the array["","b"].) If the this object
is (or converts to) the empty String, the result depends on whether
separator can match the empty String. If it can, the result array
contains no elements. Otherwise, the result array contains one
element, which is the empty String. If separator is a regular
expression that contains capturing parentheses, then each time
separator is matched the results (including any undefined results)
of the capturing parentheses are spliced into the output array.
javascript equivalent functions to php
(http://kevin.vanzonneveld.net)
<script>
function preg_split (pattern, subject, limit, flags) {
// http://kevin.vanzonneveld.net
// + original by: Marco Marchi??
// * example 1: preg_split(/[\s,]+/, 'hypertext language, programming');
// * returns 1: ['hypertext', 'language', 'programming']
// * example 2: preg_split('//', 'string', -1, 'PREG_SPLIT_NO_EMPTY');
// * returns 2: ['s', 't', 'r', 'i', 'n', 'g']
// * example 3: var str = 'hypertext language programming';
// * example 3: preg_split('/ /', str, -1, 'PREG_SPLIT_OFFSET_CAPTURE');
// * returns 3: [['hypertext', 0], ['language', 10], ['programming', 19]]
// * example 4: preg_split('/( )/', '1 2 3 4 5 6 7 8', 4, 'PREG_SPLIT_DELIM_CAPTURE');
// * returns 4: ['1', ' ', '2', ' ', '3', ' ', '4 5 6 7 8']
// * example 5: preg_split('/( )/', '1 2 3 4 5 6 7 8', 4, (2 | 4));
// * returns 5: [['1', 0], [' ', 1], ['2', 2], [' ', 3], ['3', 4], [' ', 5], ['4 5 6 7 8', 6]]
limit = limit || 0; flags = flags || ''; // Limit and flags are optional
var result, ret=[], index=0, i = 0,
noEmpty = false, delim = false, offset = false,
OPTS = {}, optTemp = 0,
regexpBody = /^\/(.*)\/\w*$/.exec(pattern.toString())[1],
regexpFlags = /^\/.*\/(\w*)$/.exec(pattern.toString())[1];
// Non-global regexp causes an infinite loop when executing the while,
// so if it's not global, copy the regexp and add the "g" modifier.
pattern = pattern.global && typeof pattern !== 'string' ? pattern :
new RegExp(regexpBody, regexpFlags+(regexpFlags.indexOf('g') !==-1 ? '' :'g'));
OPTS = {
'PREG_SPLIT_NO_EMPTY': 1,
'PREG_SPLIT_DELIM_CAPTURE': 2,
'PREG_SPLIT_OFFSET_CAPTURE': 4
};
if (typeof flags !== 'number') { // Allow for a single string or an array of string flags
flags = [].concat(flags);
for (i=0; i < flags.length; i++) {
// Resolve string input to bitwise e.g. 'PREG_SPLIT_OFFSET_CAPTURE' becomes 4
if (OPTS[flags[i]]) {
optTemp = optTemp | OPTS[flags[i]];
}
}
flags = optTemp;
}
noEmpty = flags & OPTS.PREG_SPLIT_NO_EMPTY;
delim = flags & OPTS.PREG_SPLIT_DELIM_CAPTURE;
offset = flags & OPTS.PREG_SPLIT_OFFSET_CAPTURE;
var _filter = function(str, strindex) {
// If the match is empty and the PREG_SPLIT_NO_EMPTY flag is set don't add it
if (noEmpty && !str.length) {return;}
// If the PREG_SPLIT_OFFSET_CAPTURE flag is set
// transform the match into an array and add the index at position 1
if (offset) {str = [str, strindex];}
ret.push(str);
};
// Special case for empty regexp
if (!regexpBody){
result=subject.split('');
for (i=0; i < result.length; i++) {
_filter(result[i], i);
}
return ret;
}
// Exec the pattern and get the result
while (result = pattern.exec(subject)) {
// Stop if the limit is 1
if (limit === 1) {break;}
// Take the correct portion of the string and filter the match
_filter(subject.slice(index, result.index), index);
index = result.index+result[0].length;
// If the PREG_SPLIT_DELIM_CAPTURE flag is set, every capture match must be included in the results array
if (delim) {
// Convert the regexp result into a normal array
var resarr = Array.prototype.slice.call(result);
for (i = 1; i < resarr.length; i++) {
if (result[i] !== undefined) {
_filter(result[i], result.index+result[0].indexOf(result[i]));
}
}
}
limit--;
}
// Filter last match
_filter(subject.slice(index, subject.length), index);
return ret;
}
</script>

How do I convert output of number_format back to numbers in PHP?

PHP can't recognize 1,200.00(generated by number_format) but only 1200.00,
What's the general solution for this problem?
You could remove any character that is not a digit or a decimal point and parse that with floatval:
$number = 1200.00;
$parsed = floatval(preg_replace('/[^\d.]/', '', number_format($number)));
var_dump($number === $parsed); // bool(true)
And if the number has not . as decimal point:
function parse_number($number, $dec_point=null) {
if (empty($dec_point)) {
$locale = localeconv();
$dec_point = $locale['decimal_point'];
}
return floatval(str_replace($dec_point, '.', preg_replace('/[^\d'.preg_quote($dec_point).']/', '', $number)));
}
use ;
Sanitize filters
$number1= '$ 1,989.34';
$number2 = filter_var($number, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);//1989.34
$number2 = filter_var($number, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_THOUSAND);//1,98934
If you're using 5.3 or higher (thanks ircmaxell), use numfmt_parse.
You can use filter_var. e.g.
$num = '1,200,998';
$real_integer = filter_var($num, FILTER_SANITIZE_NUMBER_INT);
echo $real_integer;
will output:
1200998
The better way is to use the options in number_format function itself. The format is
number_format ( float $number , int $decimals = 0 , string $dec_point = '.' , string $thousands_sep = ',' )
I.e. if you don't need the ',' in the formatted string make, it is like
$num=23.33333
echo number_format($num,2,'.','');
If you need that as float then typecast the same to float
$floatnum= (float) number_format($num,2,'.','');
You could do $num = (float) str_replace(',', '', $num); Basically, just get rid of the commas, and then cast it as a numeric type (float or int).
I use the following code:
function remove_non_numerics($str)
{
$temp = trim($str);
$result = "";
$pattern = '/[^0-9]*/';
$result = preg_replace($pattern, '', $temp);
return $result;
}
In this case you can, use PHP round method like: round (12.000000, 2);
I couldn't find a functioning solution that would convert any kind of formatted number back to normal number in php.
I needed it to work with any kind of number.
So it took a while, but I wrote it.
It works if the number has up to 2 decimals, but I think that's enough.
Also it will remove any currency symbol and will only allow digits, dots and commas to be passed.
function unformatNumber($number){
// works with 2 decimals max
$number = trim((string)$number);
$number = preg_replace('/&.*?;/', '', $number);
$number_b="";
$number_ar_prep=str_split($number);
$number_ar=array();
$separators_amount=0;
$numbers_after_separator=array();
$chars_after_last_separator=0;
$last_decimals=array();
$last_decimal="";
foreach ($number_ar_prep as $char) {
if((is_numeric($char) || $char=="," || $char==".")){
if(is_numeric($char)){
$number_ar[]=(float)$char;
}else{
$number_ar[]=$char;
}
}
}
foreach ($number_ar as $char) {
$char=trim($char);
$sep_increase=false;
if($char=="."){
$separators_amount++;
$sep_increase=true;
}elseif($char==","){
$separators_amount++;
$sep_increase=true;
}
$number_b.=$char;
if($separators_amount>0){
if($sep_increase){
$chars_after_last_separator=0;
$last_decimal="";
}else{
$chars_after_last_separator++;
$last_decimal.=$char;
}
$numbers_after_separator[$separators_amount]=$chars_after_last_separator;
$last_decimals[$separators_amount]=$last_decimal;
}
}
$has_decimals=0;
if(isset($numbers_after_separator[$separators_amount])){
if($numbers_after_separator[$separators_amount]<3){
$has_decimals=1;
}
}
if(!$has_decimals){
$clean_number = str_replace(",","",$number_b);
$clean_number = str_replace(".","",$clean_number);
}else{
$decimals = $last_decimals[$separators_amount];
$num_without_decimals = preg_replace('/'.$decimals.'$/', '', $number_b);
$clean_num_without_dec = str_replace(",","",$num_without_decimals);
$clean_num_without_dec = str_replace(".","",$clean_num_without_dec);
$clean_number = $clean_num_without_dec.'.'.$decimals;
}
return (float)$clean_number;
}
Example output:
(original > unformatted)
1 1
2 2
2.00 2
2,00 2
2,31 2.31
2.31 2.31
24 24
24.00 24
24,00 24
24,31 24.31
25.31 25.31
244 244
244.00 244
244,00 244
244,31 244.31
255.31 255.31
2000 2000
2000.31 2000.31
2000,31 2000.31
2.000 2000
2,000 2000
2.000,31 2000.31
2,000.31 2000.31
20000 20000
20.000 20000
20,000 20000
20.000,31 20000.31
20,000.31 20000.31
200000 200000
200.000 200000
200,000 200000
200.000,15 200000.15
204,000.31 204000.31
2000000 2000000
2.000.000 2000000
2.050,000 2050000
2.500.000,51 2500000.51
2.000,000.31 2000000.31
24,30 € 24.3
Thanks to #ircmaxell, I came up with this:
$t_sep = localeconv()['thousands_sep'];
$num = (float) str_replace($t_sep? $t_sep : ',', '', $numStr);
For input that may be either a string or a number:
function Intify( $prmIntOrStr ) { # return the argument as-is or without commas,
# depending on whether it is a number
$strTmp = str_replace( ',', '', $prmIntOrStr ) ; # drop all commas
# get whether the argument without commas is a number
$bolNum = is_numeric( $strTmp ) ;
# keep one of:
# either the argument without commas when it is yes a number,
# or the argument as-is when it is not a number.
$valRtn = ( $bolNum ) ? $strTmp : $prmIntOrStr ;
return $valRtn ; # pass result back to caller
} # Intify
# set test value ; show as-is ; call function ; show result
$IntOrStr = "String" ; echo $IntOrStr.PHP_EOL; $IntOrStr = Intify( $IntOrStr ); echo $IntOrStr.PHP_EOL.PHP_EOL;
$IntOrStr = "123" ; echo $IntOrStr.PHP_EOL; $IntOrStr = Intify( $IntOrStr ); echo $IntOrStr.PHP_EOL.PHP_EOL;
$IntOrStr = "123,456"; echo $IntOrStr.PHP_EOL; $IntOrStr = Intify( $IntOrStr ); echo $IntOrStr.PHP_EOL.PHP_EOL;
use "[^0-9.]+" regex.
$formattedVal = '$9,99,999.99';
$unformattedVal = (float)preg_replace("/[^0-9.]+/", "", $formattedVal);
var_dump($unformattedVal);
worked for me.

Categories