PHP的fgetcsv返回所有行

39
我有以下的csv文件:
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,,

当我这样做时:

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

我得到了一个含有183个元素的数组,但我期望只能得到csv文件的一行,却得到了整个文件。

我甚至尝试使用$data = fgetcsv($handle, 1000);,但仍然得到相同的结果。


1
我不理解。按设计工作,不是吗?为什么只有一行CSV会被传送过来? - Pekka
6
根据文档,“解析读取的行,按CSV格式返回包含读取字段的数组”。它应该逐行工作,而不是将整个文件读入一个巨大的索引数组中。 - Chris Muench
4
@Pekka 他的 PHP 无法将换行识别为换行符,因此将文件读取为一行,从而生成由183个元素组成的数组。 - ITroubs
2
@Chris Muench:关于“CR”;从文档中可以看到:“注意:如果PHP在读取Macintosh计算机上的文件或创建的文件时没有正确识别行尾,启用auto_detect_line_endings运行时配置选项可能有助于解决问题。” - Ekkehard.Horner
你是怎么让它工作的?被接受的答案对我不起作用 :( - Sisir
显示剩余8条评论
6个回答

108

这很可能是一个行尾问题。尝试启用auto_detect_line_endings,它会尝试确定文件的行结尾。

ini_set('auto_detect_line_endings', true);

如果那不解决问题,那么使用file命令检测行终止符的类型:

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

你可以将行尾进行转换。我不确定你使用的是什么操作系统,但有很多文件格式转换工具,例如dos2unix


7
这是我能想到的最简洁的解决方案:
$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);

输出:

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] => 
        )

)

2
这个问题的描述是$header = fgetcsv($fp);会将所有行都返回到数组中。因此,这个答案并没有回答这个问题... - Oleg Abrazhaev

1

尝试:

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

或者,您可以尝试使用str_getcsv(file_get_contents(...))


我想坚持使用 fgetcsv,而且我尝试了你的建议,但它没有起作用。 - Chris Muench

1

要一次性读取整个 CSV 文件,请使用以下代码:

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

虽然你必须使用array_shift()函数来移除第一行(如果你没有使用列键)。


处理不特定行尾的解决方法:

$data = array_map("str_getcsv", preg_split('/[\r\n]+/', file_get_contents($fn)));
var_dump($data);

但是对于跨越多行的值(显然用引号括起来),那会引起问题。 - Czechnology
如果您有其中一种CSV变体,它不将换行符编码为“\n”,那么您需要使用fgetcsv。看起来OP的问题实际上是缺少LF。 (文件函数仅在本机平台上处理该问题,而不适用于转移到U * ix系统的旧Mac文件。) - mario
哦,我还是不理解在手册中的返回一个包含读取字段的*索引数组*注释。无论我尝试什么,似乎都没有返回索引数组。 - Czechnology
是的,那个措辞有歧义。它应该写成返回一个带有“数字索引”的数组。 - mario
1
@mario 我没有尝试过你的建议,你可以考虑使用~\R+~作为更短的替代模式。或者是~\R+$~m。你需要在这里使用PREG_SPLIT_NO_EMPTY吗? - mickmackusa

1
我遇到了同样的问题,使用Mac打开由Microsoft Excel生成的CSV文件。我在不同的文件上执行了more命令,发现行结束符不同。
$ 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

我使用了

ini_set('auto_detect_line_endings', true);

之前

fgetcsv($handle, 0, ';')

并且它有效。被接受的答案是正确的。


0
如果您有这个选项,请返回并编辑您的 .csv 文件以包括 "Unix" 样式的行结尾......然后 fgetcsv 将自动按行结尾分割数组。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接