I know that this question might have been answered before. but I still haven't been able to solve it.
my data comes from a CSV file where the data looks like this 1,32 BUT i also have some text in the variable $getdata half of it is VARCHAR and the other half is DECIMAL.
but when I look at my site the data is like this 1.00
So my question is How can I change the decimal separator.
The data is located in the $getdata variable in the bottom part in the code piece
if (isset($_POST['btn-upload'])){
copy("$sourcepath/$latest_filename","$copy/$latest_filename");
// Here i split the csv file, from second line. and using the first and fourth lines as the headers.
if (($openfile = fopen("$copy/$latest_filename", "r")) !== false) {
$header1 = fgetcsv($openfile, 1000, ";"); // consume, but don't use
$csv->createPalleTable($latest_filename);
$csv->insertPalleTable($latest_filename, array_map("utf8_encode", fgetcsv($openfile, 1000, ";")));
$delimiting_row = fgetcsv($openfile, 1000, ";"); // consume, but don't use
$header2 = fgetcsv($openfile, 1000, ";"); // consume, but don't use
$csv->createCsvTable($latest_filename);
while ($getdata = fgetcsv($openfile, 1000, ";")) {
$csv->insertCsvTable($latest_filename, array_map("utf8_encode", $getdata));
}
}
}
I haven't uploaded the full code since that would be way to much code. I'm pretty sure the problem is located at the bottom of the code snippet. if you need any information about the code I'll be happy to answer.
try using number format with a simple str_replace to replace the comma's so the float can be parsed.
number_format(floatval(str_replace(',', '.', str_replace('.', '', $your_number_here))), '.', '', 2)
If you want a separate readable function instead of a one-liner:
function convert_decimal($your_number_here, $decimal_places = 2)
{
$without_thousands_dots = str_replace('.', '', $your_number_here);
$replace_comma_for_dot = str_replace(',', '.', $without_thousands_dots);
$float_number = floatval($replace_comma_for_dot);
return number_format($float_number, '.', '', $decimal_places)
}
You could skip the floatval part if you don't wish to use rounding or use the number as an actual number and just keep it at returning the $replace_comma_for_dot variable.
For reference how to parse the decimal numbers to floats so you can use number format:
Converting a number with comma as decimal point to float
Related
i am using (shuchkin/simplexlsxgen) to generate xlsx file which works fine.
My data contain decimal numbers such as (0.19) which are written in the SQL DB, but instead writting them with dott, the (simplexlsxgen) convert them to (0,19) comma decimal numbers.
is there a way to prevent changing the decimal Dott to comma, before generating?
Thanks in advance.
//convert test_bulk.csv to Xlsx
function csvToArray($csvFile)
{
$file_to_read = fopen($csvFile, 'r');
while (!feof($file_to_read))
{
$lines[] = fgetcsv($file_to_read, 1000, ';');
}
fclose($file_to_read);
return $lines;
}
//read the csv file into an array
$csvFile = 'test_bulk.csv';
$csv = csvToArray($csvFile);
Shuchkin\SimpleXLSXGen::fromArray($csv)->saveAs('final_bulk.xlsx');
by adding "\0". before the targeted string, it will turn it to be a RAW STRING. which will make this super Tool write it without changing the decimal dott to comma.
you all gotta try this amazing fast effective tool by the PHP Excel Old school Master (Sergey Shuchkin).
https://github.com/shuchkin
I have a csv that has a series of dates in it. I've taken the dates and moved them into an array($data). Specifically the data for the dates are $data[0]. I'm trying to convert them from the m/d/Y way to the UTF (Y/m/d) while writing that data to another csv(which will be the target for inserting into a db). Here's what i've tried to far:
$dt = date('Y/m/d', strtotime($data[0])); //Didn't work
//Then i tried this:
$dt = DateTime::CreateFromFormat('m/d/Y', $data[0]);
$dt = $dt->format('Y/m/d');
The second function doesn't work as i get an error: "Call to a member function format() on boolean".
I'm not sure why i'm getting that error. Some help would be greatly appreciated.
Edit: Adding code to show the writing to csv. This isn't every single possible line i used but as a quick note I'm using glob to put files into an array(csvs).
foreach($files as $file)
{
if(($handle = fopen($file, "r")) !== FALSE)
{
preg_match("/^.*([0-9]{4}-[0-9]{2}-[0-9]{2}).-.([0-9]{4}-[0-9]{2}-[0-9]{2}).*/", $file, $matches);
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE)
{
if($data[0] != preg_match("/^.*([0-9]{2}-[0-9]{2}-[0-9]{4}).*/", $file))
{
$num = count($data);
$row++;
if(!file_exists($filename))
{
$newFile = fopen($filename, "w");
fputcsv($newFile, $headers);
}
if(strlen($data[0]) == 9 || strlen($data[0]) == 10)
{
$dt = DateTime::CreateFromFormat('m/d/Y', $data[0]);
$dt = $dt->format('Y/m/d');
}else{
$dt = $data[0];
echo "Something went wrong. Check the csv";
}
fputcsv($newFile, [$defaults[0], $defaults[1], $defaults[2], $defaults[3], $defaults[4], $defaults[5], $defaults[6], $defaults[7],$defaults[8],$dt, $data[1], $data[4], $data[14], $data[7], $data[2], $data[3], $data[5], $data[6]]);
}
}
}
fclose($handle);
fclose($newFile);
}
Basically what this does is match files that have a date range in their name, store that date range in matches, check the specific rows to see if a row to find the m/d/Y row to start placing data in the new csv. Again its not every possible line of code but i think this would give you a good idea of what im doing. In the fputcsv you can see $dt there instead of data[0].
This is wrong:
if(strlen($data[0] == 9) || strlen($data[0] == 10))
== should not be inside the call to strlen(). It should be
if(strlen($data[0]) == 9 || strlen($data[0]) == 10)
Because of this, you're never going into that if block, so it never fixes up $dt.
Also, you need to assign $dt when this if test fails, so you should have:
else {
$dt = $data[0];
}
This line also makes no sense to me:
if($data[0] != preg_match("/^.*([0-9]{2}-[0-9]{2}-[0-9]{4}).*/", $file))
preg_match() returns the number of matches that were found. $file is the filename, so this is the number of times a date-like pattern is in the filename. $data[0] is the contents of the first field in the CSV -- either a date or the field heading for dates. Unless this field contains a number, it will never be equal to the result of preg_match(), so this condition will always succeed. If your intent was to skip the heading line, it won't do that, so the new file will contain headings.
If you want to test if the first field contains a date, use:
if (preg_match('/([0-9]{2}-[0-9]{2}-[0-9]{4})/', $data[0]))
BTW, there's no need for .* when matching, it matches anywhere in the string when there's no anchors.
Ahhh..... I figured it out. I'm a big fool. Excel is a parser that's just automatically tuning itself to fit the way it would be read when looking at the average excel file(american format). When i look at the data through a text editor, it shows the date formatted the way it should be. That means that with another script i'd be able to target the data properly.
I have a text file like this:
1
2
3
4
5
6
7
8
9
10
And I want to remove specific lines which numbers are in an array like this:
$myfile='txt.txt';
$remove=array(1,3,6,7,10);
//wanna remove these lines
So I tried this code but It didn't work and It just doubles the text and ruins everything:
<?php
$myfile='txt.txt';
$remove=array(1,3,5,7,10);
$lines=file($myfile);
$countline=sizeof($lines);
$data=file_get_contents($myfile);
for ($i=0; $i < $countline+1; $i++) {
if (in_array($i, $remove)) {
$editeddata=str_replace($lines[$i], "", $data);
$removeline = file_put_contents($myfile, $editeddata.PHP_EOL , FILE_APPEND | LOCK_EX);
}
}
?>
I couldn't use ((for)) properly and I think it will just ruin the text because it deletes lines one after another have been deleted and it changes the order so I should have a code to remove them all at once.
And please don't give a code to just replace numbers because the main text file is not only numbers and contains word,etc...
Thanks A lot!
You're reading the file twice (with file and file_get_contents), which I think is confusing the later code. You have everything you need with the first call - an array of all the lines in the file. You're also using str_replace to remove the content, which seems a bit dangerous if any of the content is repeated.
I'd refactor this to simply filter the array of lines based on their line-number, then write it back to the file in a single operation:
$myfile = 'txt.txt';
$remove = [1, 3, 5, 7, 10];
// Read file into memory
$lines = file($myfile);
// Filter lines based on line number (+1 because the array is zero-indexed)
$lines = array_filter($lines, function($lineNumber) use ($remove) {
return !in_array($lineNumber + 1, $remove);
}, ARRAY_FILTER_USE_KEY);
// Re-assemble the output (the lines already have a line-break at the end)
$output = implode('', $lines);
// Write back to file
file_put_contents($myfile, $output);
If the file fits in memory then you can do the simple:
$myfile='txt.txt';
$remove=array(1,3,6,7,10);
file_put_contents($myfile, implode(PHP_EOL,array_diff($file($myfile,FILE_IGNORE_NEW_LINES), $remove)));
Note: Because it's a bit ambiguous whether $remove has the content or the lines you want to remove, the above code removes the content . If you want to remove lines change array_diff($file($myfile,FILE_IGNORE_NEW_LINES), $remove) to array_diff_keys($file($myfile,FILE_IGNORE_NEW_LINES), array_flip($remove))
If your file is large then you need to resort to some sort of streaming. I suggest against reading and writing to the same file and doing something like:
$myfile='txt.txt';
$remove=array(1,3,6,7,10);
$h = fopen($myfile,"r");
$tmp = fopen($myfile.".tmp", "w");
while (($line = fgets($h)) !== false) {
if (!in_array(rtrim($line, PHP_EOL), $remove)) {
fwrite($tmp, $line);
}
}
fclose($h);
fclose($tmp);
unlink($myfile);
rename($myfile.".tmp", $myfile);
Is there a built-in/neat way to format a number (just like number_format does), but without any rounding ups/downs?
For instance, number 1234.234 should be formatted as 1,234.234 and another number 1234 should be formatted as 1,234 (i.e. without any trailing .000)
You can define simple custom function for that:
<?php
function custom_number_format($number, $decimal = '.')
{
$broken_number = explode($decimal, $number);
if (isset($broken_number[1]))
return number_format($broken_number[0]) . $decimal . $broken_number[1];
else
return number_format($broken_number[0]);
}
$n1 = '1234.234';
$n2 = '1234';
echo custom_number_format($n1);
echo '<br>';
echo custom_number_format($n2);
?>
Output is:
1,234.234
1,234
Based on the arhey's answer
TLDR ;)
You can use number_format to format the number to a fixed-width format, then use rtrim twice to remove trailing zeroes, and dot.
rtrim(rtrim(number_format($number, 3, '.', ','), '0'), '.')
Starting from the last character, rtrim removes it while it is one of those given. In our case, we remove trailing dots, then we remove an eventual trailing zero.
rtrim(rtrim(number_format(1234.123, 3, '.', ','), '0'), '.')
// returns 1,234.123
rtrim(rtrim(number_format(1234.12, 3, '.', ','), '0'), '.')
// returns 1,234.12 (1,234.120, trimmed to 1234.12)
rtrim(rtrim(number_format(1234, 3, '.', ','), '0'), '.')
// returns 1,234 (1,234.000, trimmed to 1234)
rtrim(rtrim(number_format(1200, 3, '.', ','), '0'),'.')
// returns 1,200 (1,200.000, trimmed to 1200., trimmed to 1200)
Formal form, and discussion about the parameters (notably the decimals count)
rtrim(rtrim(number_format($number, <N>, '<D>', ''), '0'), '<D>')
Where :
D is the decimal separator. To avoid locale-formatting problems, explicitly specify it
N is the maximum digits you number can have.
If you know all your numbers will have less than 3 digits, go and take N=3.
What if you don't know how many decimals are at most ? Well, things are getting more complex.
It may worth recalling (as stated in the PHP documentation) that floats are stored :
with a precision (a number of digits, without distinction whether they are before or after the decimal separator), not a number of decimals
and in their binary form, not their decimal one, and that can lead to rounding errors when reaching precision limit.
For example, floor((0.1+0.7)*10) will usually return 7 instead of the
expected 8, since the internal representation will be something like
7.9999999999999991118....
So there is no universal good value, you'll have to choose it depending on the usual scale of your data.
And that explains why there is no built-in function for that : PHP can't choose for you.
You can use function:
<?php
function getNumberFormat($number) {
$numberAr = explode('.', (string)$number);
$count = 0;
if (2 === count($numberAr)) {
$count = strlen($numberAr[1]);
}
return number_format($number, $count, ',', '.');
}
$test1 = 1234.234;
$test2 = 1234;
echo getNumberFormat($test1); //1,234.234
echo getNumberFormat($test2); //1,234
I really liked arhey's answer, but later realized it has a major flaw. A number like 2100 will get converted to 2,1 instead of 2,100.
Below is how I ended up modifying it.
public function formatDecimal($number)
{
$stringVal = strval($number); //convert number to string
$decPosition = strpos($stringVal, ".");
if ($decPosition !== false) //there is a decimal
{
$decPart = substr($stringVal, $decPosition); //grab only the decimal portion
$result = number_format($stringVal) . rtrim($decPart, ".0");
}
else //no decimal to worry about
{
$result = number_format($stringVal);
}
return $result;
}
It's not as succinct a solution as I was hoping, but in my case I put it into a view helper (I'm using ZF2) and so it's just one simple function call in my view.
Hope this is helpful for someone!
rtrim(number_format(1234.234, 3),'.0');
rtrim(number_format(1234, 3),'.0');
Let's begin with that there's no decimal type in PHP. There's float only.
And if you know how float works, then you know that it's usually not possible to store exact decimal value that you think you have, but it's an approximation. That's because you can't express most of decimal numbers in binary system.
Therefore if you say:
$number = 1234.234;
Then you have a float that is close to this value. The real value is:
1234.23399999999992360244505107402801513671875
Therefore PHP can't just guess how do you want to round it. It needs to be specified explicitly.
Thank you for taking the time to read this and I will appreciate every single response no mater the quality of content. :)
Using php, I'm trying to create a script which will delete several lines within a text file (.txt) if required, based upon whether the line starts with a 0 or a negative number. Each line within the file will always start with a number, and I need to erase all the neutral and/or negative numbers.
The main part I'm struggling with is that the content within the text file isn't static (e.g. contain x number of lines/words etc.) Infact, it is automatically updated every 5 minutes with several lines. Therefore, I'd like all the lines containing a neutral or negative number to be removed.
The text file follows the structure:
-29 aullah1
0 name
4 username
4 user
6 player
If possible, I'd like Line 1 and 2 removed, since it begins with a neutral/negative number. At points, there maybe times when there are more than two neutral/negative numbers.
All assistance is appreciated and I look forward to your replies; thank you. :) If I didn't explain anything clearly and/or you'd like me to explain in more detail, please reply. :)
Thank you.
Example:
$file = file("mytextfile.txt");
$newLines = array();
foreach ($file as $line)
if (preg_match("/^(-\d+|0)/", $line) === 0)
$newLines[] = chop($line);
$newFile = implode("\n", $newLines);
file_put_contents("mytextfile.txt", $newFile);
It is important that you chop() the newline character off of the end of the line so you don't end up with empty space. Tested successfully.
Something on these lines i guess, it is untested.
$newContent = "";
$lines = explode("\n" , $content);
foreach($lines as $line){
$fChar = substr($line , 0 , 1);
if($fChar == "0" || $fChar == "-") continue;
else $newContent .= $line."\n";
}
If the file is big, its better to read it line by line as:
$fh_r = fopen("input.txt", "r"); // open file to read.
$fh_w = fopen("output.txt", "w"); // open file to write.
while (!feof($fh_r)) { // loop till lines are left in the input file.
$buffer = fgets($fh_r); // read input file line by line.
// if line begins with num other than 0 or -ve num write it.
if(!preg_match('/^(0|-\d+)\b/',$buffer)) {
fwrite($fh_w,$buffer);
}
}
fclose($fh_r);
fclose($fh_w);
Note: Err checking not included.
file_put_contents($newfile,
implode(
preg_grep('~^[1-9]~',
file($oldfile))));
php is not particularly elegant, but still...
Load whole line into variable trim it and then check if first letter is - or 0.
$newContent = "";
$lines = explode("\n" , $content);
foreach($lines as $line){
$fChar = $line[0];
if(!($fChar == '0' || $fChar == '-'))
$newContent .= $line."\n";
}
I changed malik's code for better performance and quality.
Here's another way:
class FileCleaner extends FilterIterator
{
public function __construct($srcFile)
{
parent::__construct(new ArrayIterator(file($srcFile)));
}
public function accept()
{
list($num) = explode(' ', parent::current(), 2);
return ($num > 0);
}
public function write($file)
{
file_put_contents($file, implode('', iterator_to_array($this)));
}
}
Usage:
$filtered = new FileCleaner($src_file);
$filtered->write($new_file);
Logic and methods can be added to the class for other stuff, such as sorting, finding the highest number, converting to a sane storage method such as csv, etc. And, of course, error checking.