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) {
}
Related
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.
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
}
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
I want to know how I would read a specific line (or even specific character) with fgets() or fgetss().
In example;
data.txt-
this is data 1
this is data 2
this is data 3
How would I read only data 2?
If it's possible, that is.
Also, how can I only read the last line? Like if I were to use a+ to write at the end of the file, it'd be the newest content.
Another question:
Is it possible to read through the whole file and check if something exists? If so, how?
Thanks in advance!
For text file:
$fileName = 'file.txt';
$file = new \SplFileObject($fileName);
$file->setFlags(\SplFileObject::READ_CSV);
$seek = 1;
$file->seek($seek);
$line = $file->fgets();
In $line variable has second line from file.
How to place a file pointer in the choosen line (e.g. 1 lime from the bottom) when opening file with fopen()?
If you know the offset within the file of the data you want you could always fseek () to it. Of course the problem is discovering where you need to fseek to within the file. If you can use SEEK_END as an option then the offset will be relative to the end of the file instead of the beginning, which may be helpful for you.
Alternatively, you could use the file () function to load the file data into an array. Each element on the array represents a line of the file, so the second to last element in the array will be the item you want.
You will probably have to read your file counting line endings "\n". Something like:
function fseek_line($handle, $count) {
while ((--$count > 0) && (fgets($handle, 4096) !== false)) { }
}
If you need the last line just open in append mode fopen("file.txt", "a");
There is a SplFileObject::seek method that can position the pointer to Nth line.
Unfortunatly there is no easy way for this. Since files are not stored linewise there is no order to jump one line or several.
But check out this thread: fseek() by line, not bytes?