39

I have the following csv file:

upc/ean/isbn,item name,category,supplier id,cost price,unit price,tax 1 name,tax 1 percent,tax 2 name,tax 2 percent,quantity,reorder level,description,allow alt. description,item has serial number
,Apple iMac,Computers,,10,12,8,8,10,10,10,1,Best computer,,
,Apple iMac,Computers,,10,12,8,8,10,10,10,1,Best computer,,
,Apple iMac,Computers,,10,12,8,8,10,10,10,1,Best computer,,
,Apple iMac,Computers,,10,12,8,8,10,10,10,1,Best computer,,
,Apple iMac,Computers,,10,12,8,8,10,10,10,1,Best computer,,
,Apple iMac,Computers,,10,12,8,8,10,10,10,1,Best computer,,
,Apple iMac,Computers,,10,12,8,8,10,10,10,1,Best computer,,
,Apple iMac,Computers,,10,12,8,8,10,10,10,1,Best computer,,
,Apple iMac,Computers,,10,12,8,8,10,10,10,1,Best computer,,
,Apple iMac,Computers,,10,12,8,8,10,10,10,1,Best computer,,
,Apple iMac,Computers,,10,12,8,8,10,10,10,1,Best computer,,
,Apple iMac,Computers,,10,12,8,8,10,10,10,1,Best computer,,

When I do this:

if (($handle = fopen($_FILES['file_path']['tmp_name'], "r")) !== FALSE) 
{
    $data = fgetcsv($handle);
    var_dump($data);
}

I get an array with 183 elements, I would expect to only get one line of the csv, but I get the whole file.

I even tried $data = fgetcsv($handle, 1000); and I got the same result

Czechnology
  • 14,832
  • 10
  • 62
  • 88
Chris Muench
  • 17,444
  • 70
  • 209
  • 362
  • 1
    I don't understand. Works as designed, doesn't it? Why would only one line of the CSV to come through? – Pekka Apr 04 '11 at 13:24
  • 6
    according to the documentation "parses the line it reads for fields in CSV format and returns an array containing the fields read". It is suppose to work line by line. Not an entire file into one giant indexed array. – Chris Muench Apr 04 '11 at 13:33
  • @Chris aah, I see. I thought by the 183 elements, you mean 183 lines. Are you sure the array is linear though? They should be nested: One array per line, each containing the proper columns – Pekka Apr 04 '11 at 13:34
  • @Chris what lineseparation do you use? for me this works perfectly fine and reads only the first line and dumps the array generated from the first line – ITroubs Apr 04 '11 at 13:35
  • 4
    @Pekka his php doesn't recognise his line separation as lineseparation thus reading the file as ONE line and thus generating an array consisting of 183 elements – ITroubs Apr 04 '11 at 13:36
  • @iTroubs that is indeed the most likely explanation. – Pekka Apr 04 '11 at 13:37
  • @Pekka it is the only possible if it works fine for me only by copying and running it locally on my php server. – ITroubs Apr 04 '11 at 13:38
  • CR is my line ending. I need it to work on CR, CRLF, and LF. It does work for CRLF and LF – Chris Muench Apr 04 '11 at 13:39
  • @Chris Muench: I agree with your interpretation of the docs (http://php.net/manual/en/function.fgetcsv.php); after looking at the user comments, I'd suspect your problem is caused by the encoding or the line endings of your file. – Ekkehard.Horner Apr 04 '11 at 13:40
  • @Chris Muench are you using a mac? – ITroubs Apr 04 '11 at 13:42
  • 2
    @Chris Muench: wrt "CR"; from the docs: " Note: If PHP is not properly recognizing the line endings when reading files either on or created by a Macintosh computer, enabling the auto_detect_line_endings run-time configuration option may help resolve the problem." – Ekkehard.Horner Apr 04 '11 at 13:43
  • @Ekkehard.Horner that's exactly what i wanted to say, too. – ITroubs Apr 04 '11 at 13:43
  • how did you get it to work? The accepted answer doesn't work for me :( – Sisir Jan 20 '14 at 11:12

6 Answers6

108

This is most likely a line ending issue. Try enabling auto_detect_line_endings which will attempt to determine the file's line endings.

ini_set('auto_detect_line_endings', true);

If that doesn't resolve the issue, then detect the type of line terminators using the file command:

$ file example.csv
example.csv: ASCII text, with CR line terminators

You can then convert the line endings. I am not sure what OS you are using but there are a lot of utilities out there for file format conversion, e.g. dos2unix.

Wolfram
  • 8,044
  • 3
  • 45
  • 66
mholzmann
  • 1,289
  • 1
  • 8
  • 4
7

This is the shortest solution I could think of:

$fp = fopen('test.csv', 'r');

// get the first (header) line
$header = fgetcsv($fp);

// get the rest of the rows
$data = array();
while ($row = fgetcsv($fp)) {
  $arr = array();
  foreach ($header as $i => $col)
    $arr[$col] = $row[$i];
  $data[] = $arr;
}

print_r($data);

Output:

Array
(
    [0] => Array
        (
            [upc/ean/isbn] => 
            [item name] => Apple iMac
            [category] => Computers
            [supplier id] => 
            [cost price] => 10
            [unit price] => 12
            [tax 1 name] => 8
            [tax 1 percent] => 8
            [tax 2 name] => 10
            [tax 2 percent] => 10
            [quantity] => 10
            [reorder level] => 1
            [description] => Best computer
            [allow alt. description] => 
            [item has serial number] => 
        )

    [1] => Array
        (
            [upc/ean/isbn] => 
            [item name] => Apple iMac
            [category] => Computers
            [supplier id] => 
            [cost price] => 10
            [unit price] => 12
            [tax 1 name] => 8
            [tax 1 percent] => 8
            [tax 2 name] => 10
            [tax 2 percent] => 10
            [quantity] => 10
            [reorder level] => 1
            [description] => Best computer
            [allow alt. description] => 
            [item has serial number] => 
        )

    // ...

    [11] => Array
        (
            [upc/ean/isbn] => 
            [item name] => Apple iMac
            [category] => Computers
            [supplier id] => 
            [cost price] => 10
            [unit price] => 12
            [tax 1 name] => 8
            [tax 1 percent] => 8
            [tax 2 name] => 10
            [tax 2 percent] => 10
            [quantity] => 10
            [reorder level] => 1
            [description] => Best computer
            [allow alt. description] => 
            [item has serial number] => 
        )

)
Czechnology
  • 14,832
  • 10
  • 62
  • 88
  • 2
    The issue described in this question is that `$header = fgetcsv($fp);` will return ALL lines in the array. So, this answer doesn't answer the question... – Oleg Abrazhaev Oct 27 '17 at 14:33
1

Got the same problem, on a Mac, with a CSV file generated by Microsoft Excel. I executed the command more on different files, and it appears that the line ending differ.

$ more excel.csv && more test.csv
aaa@aaa.fr^Mbbb@bbb.fr^Mccc@ccc.fr^M
ddd@ddd.fr
eee@eee.fr
fff@fff.fr

I used

ini_set('auto_detect_line_endings', true);

before

fgetcsv($handle, 0, ';')

and it works. The accepted answer is correct.

1

try:

 while (($data = fgetcsv($handle, 0, ',')) !== FALSE) {
    $columns = count($data);
 }

Alternatively, you could try using str_getcsv(file_get_contents(...))

Martin
  • 6,632
  • 4
  • 25
  • 28
1

To read in the whole CSV file at once use:

$data = array_map("str_getcsv", file($fn));
var_dump($data);

Though you will have to array_shift() the first row (if you don't use the column keys).


The workaround for unspecific line endings:

$data = array_map("str_getcsv", preg_split('/[\r\n]+/', file_get_contents($fn)));
var_dump($data);
mario
  • 144,265
  • 20
  • 237
  • 291
  • But what about the values that span over multiple lines (enclosed in quotes, obviously). That would cause problems. – Czechnology Apr 04 '11 at 13:41
  • If you have one of the CSV variants that doesn't encode the line breaks as `\n` then yes, you would need fgetcsv instead. Seems OPs problem is actually lack of LFs. (And the file functions only handle that on the native plattform, not for old Mac files transferred onto U*ix systems.) – mario Apr 04 '11 at 13:44
  • Hmm... I still doesn't understand the `Returns an *indexed array* containing the fields read.` note in the [manual](http://php.net/manual/en/function.fgetcsv.php). Doesn't seem to return indexed array no matter what I try. – Czechnology Apr 04 '11 at 13:47
  • Yeah, that's an ambiguous phrasing. It should read that it returns an array with `*numeric indexes*`. – mario Apr 04 '11 at 13:50
  • 1
    @mario I didn't try your suggestion, You might entertain `~\R+~` as a shorter alternative pattern. Or maybe `~\R+$~m`. Do you need to use `PREG_SPLIT_NO_EMPTY` with this? – mickmackusa Aug 05 '19 at 00:06
0

If you have the option, go back an edit your .csv file to include "Unix" style line endings...then fgetcsv will automatically break the array up by line endings

Dr Nick Engerer
  • 765
  • 7
  • 10