将PHP CSV字符串转换为数组

61

我正在尝试在PHP中将CSV字符串解析为数组。CSV字符串具有以下属性:

Delimiter: ,
Enclosure: "
New line: \r\n

示例内容:

"12345","Computers","Acer","4","Varta","5.93","1","0.04","27-05-2013"
"12346","Computers","Acer","5","Decra","5.94","1","0.04","27-05-2013"

当我尝试这样解析它:

$url = "http://www.url-to-feed.com";
$csv = file_get_contents($url);
$data = str_getcsv($csv);
var_dump($data);

最后一个元素和第一个元素被连接成一个字符串:

[0]=> string(5) "12345"
...
[7]=> string(4) "0.04"
[8]=> string(19) "27-05-2013
"12346""

我该如何修复这个问题?任何帮助都将不胜感激。


2
"[2]=> string(48) "Acer"",Acer 明显不可能有 48 个字符长。如果您的输入格式正确,您可能需要检查一下。 - dev-null-dweller
10个回答

149

这样做:

$csvData = file_get_contents($fileName);
$lines = explode(PHP_EOL, $csvData);
$array = array();
foreach ($lines as $line) {
    $array[] = str_getcsv($line);
}
print_r($array);

它将会给你一个像这样的输出:

Array
(
    [0] => Array
        (
            [0] => 12345
            [1] => Computers
            [2] => Acer
            [3] => 4
            [4] => Varta
            [5] => 5.93
            [6] => 1
            [7] => 0.04
            [8] => 27-05-2013
        )

    [1] => Array
        (
            [0] => 12346
            [1] => Computers
            [2] => Acer
            [3] => 5
            [4] => Decra
            [5] => 5.94
            [6] => 1
            [7] => 0.04
            [8] => 27-05-2013
        )

)

我希望这能对你有所帮助。


2
我真的很好奇,这个解决方案有什么优点比Gaui在下面提供的简单一行代码更好,还是人们只是因为它是被选中的答案而投票支持它? - Mike
28
多行的CSV字段会导致翻译出现问题。 - Sebastian Mach
3
这可能会导致一个罕见的问题,如果您在值中包含换行符,则 PHP_EOL 会将该值拆分为两个不同的数组值。 - Anton
1
我的答案不会因为字段内的换行而失败。 - iateadonut
3
仅使用fgetcsv()!因为explode(PHP_EOL, $csvData)无法正确解析CSV字符串中的换行符!请参见以下正确答案。 - Modder
显示剩余5条评论

60

你应该使用 fgetcsv。因为 CSV 是一个变量,所以你无法将其作为流导入文件,那么你应该先通过使用 php://tempphp://memory 将字符串伪装为文件:

$fp = fopen("php://temp", 'r+');
fputs($fp, $csvText);
rewind($fp);

那么您使用fgetcsv不会有任何问题:

$csv = [];
while ( ($data = fgetcsv($fp) ) !== FALSE ) {
    $csv[] = $data;
}
fclose($fp)

$data将是单个CSV行的数组(可能包含换行符或逗号等),正如应该的那样。

注意:可以通过追加/maxmemory:NN来控制php://temp的内存限制,其中NN是在使用临时文件之前保留在内存中的最大数据量,以字节为单位。(默认为2 MB)http://www.php.net/manual/en/wrappers.php.php


14
这是少数几个能够正确处理字段内换行的答案之一,它应该有更多的赞。 - Nick Coons
7
这是最好的方法!不要使用file('data.csv')或者explode(PHP_EOL, $csv),因为它们无法正确解析CSV字符串中的换行符! - Modder
1
它如何区分字段内的换行符和构成行/记录分隔符的换行符? - silverdr
这是更为适当的方式,被接受的答案是一种快速的方式,但不涵盖字段中的多行。 - Long M K Nguyễn
php://temp的内存限制可以通过附加/maxmemory:NN来控制,其中NN是在使用临时文件之前在内存中保留的最大数据量(以字节为单位)。 (默认值为2 MB)https://www.php.net/manual/en/wrappers.php.php - 谢谢,史蒂文! - iateadonut
显示剩余2条评论

37

方便的一行代码:

$csv = array_map('str_getcsv', file('data.csv'));

16
对于多行CSV字段来说,它行不通,对吗? - Sebastian Mach
2
无法处理字段内的换行符。接受的答案通过 PHP_EOL 解决了这个问题。如果没有换行符,那么这很好。感谢您的努力。 - Webmaster G
3
需要翻译的内容: "Should credit the author of this handy one liner: http://php.net/manual/en/function.str-getcsv.php (see first comment)"应该给这个方便实用的一行代码的作者留下功劳:http://php.net/manual/en/function.str-getcsv.php(请参见第一个评论)。 - zanderwar
1
只使用 fgetcsv()!因为 file('data.csv') 无法正确解析 CSV 字符串中的换行符!请参见下面的正确答案。 - Modder
2
@Alex 在 https://secure.php.net/manual/en/function.str-getcsv.php 方法的文档中,详细介绍了如何更改分隔符(例如使用分号而不是逗号),以及封闭和转义。因此,该方法同样有效,只需要深入挖掘并阅读文档。PHP文档编写得非常好,并且有大量示例。 - xarlymg89
显示剩余3条评论

9
我使用以下函数将csv字符串解析为关联数组:
public function csvToArray($file) {
    $rows = array();
    $headers = array();
    if (file_exists($file) && is_readable($file)) {
        $handle = fopen($file, 'r');
        while (!feof($handle)) {
            $row = fgetcsv($handle, 10240, ',', '"');
            if (empty($headers))
                $headers = $row;
            else if (is_array($row)) {
                array_splice($row, count($headers));
                $rows[] = array_combine($headers, $row);
            }
        }
        fclose($handle);
    } else {
        throw new Exception($file . ' doesn`t exist or is not readable.');
    }
    return $rows;
}

如果您的CSV文件名为mycsv.csv,则调用此函数的方法如下:

$dataArray = csvToArray(mycsv.csv);

你可以在http://www.scriptville.in/parse-csv-data-to-array/获取这个脚本。

1
这是目前为止最好的解决方案!由于我正在使用Excel CSV,因此我已更改了此行$row = fgetcsv($handle, 10240, ',', '"');$row = fgetcsv($handle, 10240, ';', '"'); - Vladimir Jovanović

3
稍微简短的版本,不需要第二个无用变量:
$csv = <<<'ENDLIST'
"12345","Computers","Acer","4","Varta","5.93","1","0.04","27-05-2013"
"12346","Computers","Acer","5","Decra","5.94","1","0.04","27-05-2013"
ENDLIST;

$arr = explode("\n", $csv);
foreach ($arr as &$line) {
  $line = str_getcsv($line);
}

4
无法处理多行文本字段。 - Sebastian Mach

3
使用 array_map 修改之前的答案。
将 CSV 数据分解成多行。
$csv = array_map('str_getcsv', explode("\n", $csvData));

1

尝试这个,对我来说它是有效的:

$delimiter = ",";
  $enclosure = '"';
  $escape = "\\" ;
  $rows = array_filter(explode(PHP_EOL, $content));
  $header = NULL;
  $data = [];

  foreach($rows as $row)
  {
    $row = str_getcsv ($row, $delimiter, $enclosure , $escape);

    if(!$header) {
      $header = $row;
    } else {
      $data[] = array_combine($header, $row);
    }
  }

array_combine(): 两个参数应该具有相等数量的元素,我该如何纠正并检查它们是否是2个关联数组,并返回一个新的关联数组。 - JG_GJ

0

如果列中有回车符/换行符,str_getcsv 将无法正常工作。

请尝试 https://github.com/synappnz/php-csv

使用:

include "csv.php";
$csv = new csv(file_get_contents("filename.csv"));
$rows = $csv->rows();
foreach ($rows as $row)
{
  // do something with $row
}

0
如果你需要CSV的列名,可以使用这个方法。
 $example= array_map(function($v) {$column = str_getcsv($v, ";");return array("foo" => $column[0],"bar" => $column[1]);},file('file.csv'));

0
你可以使用这个函数将CSV字符串转换为数组。
    function csv2array(
        $csv_string,
        $delimiter = ",",
        $skip_empty_lines = true,
        $trim_fields = true,
        $FirstLineTitle = false
    ) {
        $arr = array_map(
            function ( $line ) use ( &$result, &$FirstLine, $delimiter, $trim_fields, $FirstLineTitle ) {
                if ($FirstLineTitle && !$FirstLine) {
                    $FirstLine = explode( $delimiter, $result[0] );
                }
                $lineResult = array_map(
                    function ( $field ) {
                        return str_replace( '!!Q!!', '"', utf8_decode( urldecode( $field ) ) );
                    },
                    $trim_fields ? array_map( 'trim', explode( $delimiter, $line ) ) : explode( $delimiter, $line )
                );
                return $FirstLineTitle ? array_combine( $FirstLine, $lineResult ) : $lineResult;
            },
            ($result = preg_split(
                $skip_empty_lines ? ( $trim_fields ? '/( *\R)+/s' : '/\R+/s' ) : '/\R/s',
                preg_replace_callback(
                    '/"(.*?)"/s',
                    function ( $field ) {
                        return urlencode( utf8_encode( $field[1] ) );
                    },
                    $enc = preg_replace( '/(?<!")""/', '!!Q!!', $csv_string )
                )
            ))
        );
        return $FirstLineTitle ? array_splice($arr, 1) : $arr;
    }

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