Read a csv file and create another csv file using php - php

I have a .csv file and when I open it via notepad it looks like:
ID501501503502
And when I print it in the browser via php, it looks like
ID 501 501 503 502
My code is:
$handle = fopen("MonC1.csv", "r");
$data = fgetcsv($handle, 0);
$fh = fopen('php://output', 'w');
if (! empty($data)) {
foreach ($data as $item) {
echo $item."<br>";
//fputcsv($fh, array($item));
}
}
So basically the br tag inside the for loop doesn't work.This is my first problem.
And the second problem is even if I use fputcsv(now it is turned off) it doesn't create a actual csv file.
Now my question is how come it has got no space in notepad and when I print in browser it gets space?
I have only one column and nothing else.
Any help is highly appreciated. Thanks in advance.

As mentioned in my comment, it looks like your original file is saved with UNIX line endings, and then you're using fgetcsv in Windows, so the whole file is being read as one line. There's probably one <br> at the end of the whole output.
So before processing your file, you'll need to preprocess it to convert the line endings.
On Linux, the utility unix2dos does what you want. Or, you can simply replace \n with \r\n in your file.
The other problem is that fgetcsv does not turn an entire file into an array. Instead, it reads a single line from your file handle, and converts that to an array. You'll need to read lines inside a loop:
while (($data = fgetcsv($handle)) !== FALSE) {
// $data contains one line of the CSV, in array form
// now you can fputcsv the single line
}

Related

PHP write to file without last newline

I want to read a CSV data file, load it into an array, edit it and write it back to a file. I have been able to accomplish this a single iteration with examples here on Stackoverflow! Thanks.
The trouble is when I write the new data back to the file, both methods I have tried to write the edited Array back to the file add an newline at the end the file. This creates an issue when loading the CSV file data a 2nd time. The 2nd read causes an empty Index in the Array that causes an error when writing the file.
Example #1:
foreach($editArray as $row) {
$writeStuff = implode(",", $row);
fwrite($file_handle, $writeStuff);
fwrite($file_handle, "\n");
}
Example #2:
foreach ($editArray as $row) {
fputcsv($file_handle, $row);
}
This is the original csv data:
1/1/16,Yes,No
1/2/16,No,Yes
When written using the above it produces this data with the added newline:
1/1/16,Yes,No
1/2/16,No,Yes
This extra new line creates an issue when reading the file a 2nd time. I get an error on both the fputcsv() or implode(). I believe it is because the empty Index caused by the newline when I read the file the 2nd time after the first write.
I could use a for loop with a conditional on the last fwrite() in the implode() Example #1, but that would seem clunky and not the way to do it.
Maybe there is a completely different way to handle this?
This is the expected behaviour of fputcsv
fputcsv() formats a line (passed as a fields array) as CSV and write it (terminated by a newline) to the specified file handle.
Being that all lines are terminated by newline, you will have an extra blank line at the end of the file
You should apply a fix for the second read, where the last line creates issues, by checking if the line is empty before processing.
If you want to prevent adding a new line at the end of the file, you could build your data set with new lines where you need them (and where you don't) then write it once:
$writeStuff = [];
foreach($editArray as $row) {
$writeStuff[] = implode(',', $row);
}
fwrite($file_handle, implode(PHP_EOL, $writeStuff));
Also, I'm not sure how you load the file, but you could always skip empty lines - here's an example:
$editArray = file('your_filename.csv', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
Based upon the recommendation, I looked for a solution when reading and loading the file rather than when I wrote the file.
These are the solutions I came up with.
First Option:
while(! feof($file_handle)) {
$tmp = fgetcsv($file_handle);
if($tmp != NULL) {
$myArray[] = $tmp;
}
}
fgetcsv returns a NULL if the line is empty.
Second Option. Ditch the fgetcsv() for file(). It ignores the empty newline without testing.
$data_Array = file($file);
foreach($$data_Array as $key) {
$myArray[] = explode(",", $key);
}
This seems to work. Additionally the example given earlier with implode() and PHP_EOL seems to work also. I may be missing something, but these work for now.

Storing last line of file in an array continuously in PHP

So i have a little issue with some PHP read functionality. What I am trying to do is basically grab data into an array from a file that is being continuously updated from a python script reading values from a micro controller. So basically, the file would look something like this.
ID, Datetime, Count, Name
ID, Datetime, Count, Name
ID, Datetime, Count, Name
What i need is for it to read the new line that is being added in (eof) and store it into an array. So what i have so far is allowing read access into the file
<?php
$myfile = fopen("read.txt", "r")
For the storing the lines in an array i figured something like an array map would be efficient
$result = array();
// some loop
$parts = array_map('trim', explode(':', $line_of_text, 2)));
$result[$parts[0]] = $parts[1];
However i am not to sure on how to structure the loop to have it read the new line that is being updated in the file without exiting the loop.
while (feof($file)) {
}
fclose($file);
?>
Any help would be appreciated!!
Can you do this?
Read the lines of the file to an array using $lines = file("filename");.
Use the $lines[count($lines) - 1] to get the last line?
You can even trim off the empty lines before you wanna do this.
Trim Empty Lines
Use this function:
$lines = array_filter($lines);
Since the file is continually being appended, you'd have to read until you hit the end of file, sleep for a while to let more data be appended, then read again.
e.g.
while(true) {
while(!feof($file)) {
... process data
}
sleep(15); // pause to let more data be appended
}
However, I'm not sure if PHP will cache the fact that it hit eof, and not try again once the sleep() finishes. It may be necessary to record your current position ftell(), close the file, reopen it, then fseek() to the stored location.
I've came up with this solution
$filename = "file.txt";
$file = fopen($filename, "r");
$lines = explode("/n", fread($file, filesize($filename)));
$last = $lines[count($lines)-1];
If the file is going to get big, it could take some time to parse, so its also possible to adjust the fread() function so it only reads the last 100 characters for example.

What is the most efficient PHP way to read first and last line of a file?

I'm trying to open a file and determine if it is valid. It's valid if the first line is START and the last line is END.
I've seen different ways of getting the last line of a file, but it does not pay particular attention to the first line either.
How should I go about this? I was thinking of loading the file contents in an array and checking $array[0] and $array[x] for START and END. But this seems to be a waste for all the junk that could possibly be in the middle.
If its a valid file, I will be reading/processing the contents of the file between START and END.
Don't read entire file into an array if it is not needed. If file can be big you can do it that way:
$h = fopen('text.txt', 'r');
$firstLine = fgets($h);
fseek($h, -3, SEEK_END);
$lastThreeChars = fgets($h);
Memory footprint is much lower
That's from me:
$lines = file($pathToFile);
if ($lines[0] == 'START' && end($lines) == 'END') {
// do stuff
}
Reading whole file with fgets will be efficient for small siles. iF ur file is big then:
open It and read first line
use tail (i didn't check it but it looks OK) function I found in php.net in fseek documentation

fputcsv adds a line ending on the last element

I have a basic PHP script that creates a csv file from an array. Here is an example of the code:
$array = [
[1,2,3],
[4,5,6]
];
$handle = fopen('test.csv', 'w');
foreach($array as $v)
fputcsv($handle, $v);
fclose($handle);
The resulting file always has a blank line at the end of the file, because fputcsv doesn't know that this is the last line. Any (simple) ideas on how to prevent this?
EDIT:
The original question is now irrelevant (to me, but maybe someone will need to do this). fputcsv is supposed to add a new line, even at the end of the document, and this is the expected behavior of all csv files.
I marked the answer that solves the original question, even though it isn't relevant to me anymore.
So in my context, I needed to check if the last line (or any line) of the array is NULL (otherwise PHP will through up a Warning that fputcsv's 2nd parameter is null). Here is my updated script if anyone is interested:
$array = [
[1,2,3],
[4,5,6]
];
$handle = fopen('test.csv', 'w');
foreach($array as $v)
if($v != NULL)
fputcsv($handle, $v);
fclose($handle);
I found this solution on another question: https://stackoverflow.com/a/8354413/1564018
$stat = fstat($handle);
ftruncate($handle, $stat['size']-1);
I added these two lines after fputcsv() and they removed the last new line character, removing the blank line at the end of the file.
Add exit(); at the end, after fclose(handle);
Like #Barmar says, all lines have a line ending, let's say \n. What you see as a blank line at the end of the file is your editor doing that. A truly blank line is two line-ending characters in succession. (\n\n for example)
Imagine you have a blank file:
(EOF)
If you fputcsv this: array(0,"hello",3323) you get
(0,hello,3323\nEOF)
given PHP docs:
fputcsv() formats a line (passed as a fields array) as CSV and write it (terminated by a newline) to the specified file handle.
A blank line in your editor is expected then. But in the file there's no such thing.
fputcsv adds a new line after each line, that's how it works. From the docs:
fputcsv() formats a line (passed as a fields array) as CSV and write it (terminated by a newline)
A new line at the end of a file is not an error, or something you need to worry about. Don't try to remove it, just leave it.
In the comments, you mention you got an error:
Warning: fputcsv() expects parameter 2 to be array, boolean given
This is probably because you are not using fgetcsv correctly. It returns FALSE when it hits the end of the file (the new line). The docs show you how to use it correctly:
while (($data = fgetcsv($handle)) !== FALSE) {
}

Verifying a CSV file is really a CSV file

I want to make sure a CSV file uploaded by one of our clients is really a CSV file in PHP. I'm handling the upload itself just fine. I'm not worried about malicious users, but I am worried about the ones that will try to upload Excel workbooks instead. Unless I'm mistaken, an Excel workbook and a CSV can still have the same MIME, so checking that isn't good enough.
Is there one regular expression that can handle verifying a CSV file is really a CSV file? (I don't need parsing... that's what PHP's fgetcsv() is for.) I've seen several, but they are usually followed by comments like "it didn't work for case X."
Is there some other better way of handling this?
(I expect the CSV to hold first/last names, department names... nothing fancy.)
Unlike other file formats, CSV has no tell-tale bytes in the file header. It starts straight away with the actual data.
I don't see any way except to actually parse it, and to count whether there is the expected number of columns in the result.
It may be enough to read as many characters as are needed to determine the first line (= until the first line break).
You can write a RE that will give you a guess if the file is valid CSV or not - but perhaps a better approach would be to try and parse the file as if it was CSV (with your fgetcsv() call), and assume it's NOT a valid one if the call fails?
In other words, the best way to see if the file is a valid CSV file is to try and parse it as such, and assume that if you failed to parse, it wasn't a CSV!
The easiest way is to try parsing the CSV and attempting to read value from it. Parse it using str_getcsv and then attempt to read a value from it. If you are able to read and validate at least a couple of values, then the CSV is valid.
EDIT
If you don't have access to str_getcsv, use this, a drop-in replacement for str_getcsv from http://www.electrictoolbox.com/php-str-getcsv-function/:
if (!function_exists('str_getcsv')) {
function str_getcsv($input, $delimiter = ",", $enclosure = '"', $escape = "\\") {
$fp = fopen("php://memory", 'r+');
fputs($fp, $input);
rewind($fp);
$data = fgetcsv($fp, null, $delimiter, $enclosure); // $escape only got added in 5.3.0
fclose($fp);
return $data;
}
}
Technically speaking, almost any text file could be a CSV file (barring quotes that don't match, etc.). You can try to guess if it's a binary file, but there isn't a reliable way to do that unless your data only has ASCII or something of the sort. If all you care is that people don't upload Excel files by mistake, check the file extension.
Any text file is a valid CSV file so it is impossible to come up with a standard way of verifying its correctness because it depends on what you really expect it to be.
Before you even start, you have to know what delimiter is used in that CSV file. After that, the easiest way to verify is to use fgetcsv function. For example:
<?php
$row = 1;
if (($handle = fopen("test.csv", "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
$num = count($data); // Number of fields in a row.
if ($num !== 5)
{
// OMG! Column count is not five!
}
else if (intval($data[$c]) == 0)
{
// OMG! Customer thinks we sold a car for $0!
}
}
fclose($handle);
}
?>

Categories