如何使用 PHP 解析 CSV 文件 [重复]
Posted
技术标签:
【中文标题】如何使用 PHP 解析 CSV 文件 [重复]【英文标题】:How to parse a CSV file using PHP [duplicate] 【发布时间】:2012-02-26 16:38:58 【问题描述】:假设我有一个.csv
文件,其内容如下:
"text, with commas","another text",123,"text",5;
"some without commas","another text",123,"text";
"some text with commas or no",,123,"text";
如何通过 php 解析内容?
【问题讨论】:
您基本上是在问是否有比股票全局函数方法更好的 OOP 方法来处理 CSV 解析。我想说改写这个问题,因为这听起来不像是解析 CSV 的问题。 @quickshiftin 对此感到抱歉 没关系,我只是说......如果你想要一个课程this one 没问题(我在我的工作中对其进行了一些调整......) 【参考方案1】:只需使用解析CSV文件的功能
http://php.net/manual/en/function.fgetcsv.php
$row = 1;
if (($handle = fopen("test.csv", "r")) !== FALSE)
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE)
$num = count($data);
echo "<p> $num fields in line $row: <br /></p>\n";
$row++;
for ($c=0; $c < $num; $c++)
echo $data[$c] . "<br />\n";
fclose($handle);
【讨论】:
需要注意的是这个函数不能正确处理CSV中的引号。具体来说,它无法处理***中的这个示例:en.wikipedia.org/wiki/Comma-separated_values#Example 存在一个未解决的错误,但它已被关闭为“无法修复”bugs.php.net/bug.php?id=50686 在内容中有换行符的列上也不能正常工作【参考方案2】:自PHP >= 5.3.0以来的答案有点短:
$csvFile = file('../somefile.csv');
$data = [];
foreach ($csvFile as $line)
$data[] = str_getcsv($line);
【讨论】:
请注意,如果您在实际值中有任何换行符(不是每个 csv 行末尾的行分隔符),这将不起作用,因为file
函数在换行符上拆分并且不是不知道 CSV 用于包含字段值的引号。
@JordanLev 那么你有什么推荐的呢?
@Julix 使用 the accepted answer 。如果您知道导入的数据在单个值中永远不会有换行符,那么这个较短的版本会很好,但是更强大的解决方案值得额外的代码行。
我最终在保存为 CSV 并在读取时解码之前进行了编码 - php.net/rawurlencode - 确保没有换行符,对吧? - 还是完全松动它们(URL 编码中可以有换行符吗?)
这个答案完全适用于换行并使用 php 文件功能的魔力。谢谢【参考方案3】:
一个方便的衬垫将 CSV 文件解析成一个数组
$csv = array_map('str_getcsv', file('data.csv'));
【讨论】:
请注意,如果您在实际值中有任何换行符(不是每个 csv 行末尾的行分隔符),这将不起作用,因为file
函数在换行符上拆分并且不是不知道 CSV 用于包含字段值的引号。
如何使用不同的分隔符? ( ; 而不是 , )
使用以下方法解决换行问题:array_map('str_getcsv', file('data.csv' , FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES));
你知道,很高兴给各自的作者致谢:php.net/manual/en/function.str-getcsv.php#114764
@Maxim Kovalevsky,如何跳过标题或第一行【参考方案4】:
刚刚发现了一种在解析时获取索引的便捷方法。我的心被炸毁了。
$handle = fopen("test.csv", "r");
for ($i = 0; $row = fgetcsv($handle ); ++$i)
// Do something will $row array
fclose($handle);
来源:link
【讨论】:
我们的服务器有 PHP 5.2.9,因此我无法访问 str_getcsv。另一方面,fgetcsv 可以追溯到 PHP 4,所以这很有帮助。谢谢。 只是想知道为什么使用 for 循环而不是 while 循环? @valerie 我在解析 CSV 时几乎总是需要索引。 for 循环提供了没有单独声明和增量器的索引。【参考方案5】:我喜欢这个
$data = str_getcsv($CsvString, "\n"); //parse the rows
foreach ($data as &$row)
$row = str_getcsv($row, "; or , or whatever you want"); //parse the items in rows
$this->debug($row);
在我的情况下,我将通过 Web 服务获取 csv,因此我不需要创建文件。但是如果你需要解析一个文件,它只需要作为字符串传递
【讨论】:
这对我来说不能正常工作。例如,这个:aaa,bbb,"ccc\nddd",eee
被解析为两行(而不是所需的一行)而不是一行。当"
出现在字段内时(而不是在其开头或结尾),它似乎未被识别为外壳。所以$data = str_getcsv(..)
可以换成$data = explode(..)
,我猜这样效率更高,也能更好地传达意图……【参考方案6】:
我一直在寻找同样的东西,但没有使用一些不受支持的 PHP 类。 Excel CSV 并不总是使用引号分隔符,而是使用 "" 转义引号,因为该算法可能是在 80 年代或其他时间制作的。在查看了 PHP.NET 的 cmets 部分中的几个 .csv 解析器之后,我看到了一些甚至使用回调或 eval 代码的解析器,它们要么无法正常工作,要么根本无法正常工作。因此,我为此编写了自己的例程,它们在最基本的 PHP 配置中工作。数组键可以是数字或命名为标题行中给出的字段。希望这会有所帮助。
function SW_ImplodeCSV(array $rows, $headerrow=true, $mode='EXCEL', $fmt='2D_FIELDNAME_ARRAY')
// SW_ImplodeCSV - returns 2D array as string of csv(MS Excel .CSV supported)
// AUTHOR: tgearin2@gmail.com
// RELEASED: 9/21/13 BETA
$r=1; $row=array(); $fields=array(); $csv="";
$escapes=array('\r', '\n', '\t', '\\', '\"'); //two byte escape codes
$escapes2=array("\r", "\n", "\t", "\\", "\""); //actual code
if($mode=='EXCEL')// escape code = ""
$delim=','; $enclos='"'; $rowbr="\r\n";
else //mode=STANDARD all fields enclosed
$delim=','; $enclos='"'; $rowbr="\r\n";
$csv=""; $i=-1; $i2=0; $imax=count($rows);
while( $i < $imax )
// get field names
if($i == -1)
$row=$rows[0];
if($fmt=='2D_FIELDNAME_ARRAY')
$i2=0; $i2max=count($row);
while( list($k, $v) = each($row) )
$fields[$i2]=$k;
$i2++;
else //if($fmt='2D_NUMBERED_ARRAY')
$i2=0; $i2max=(count($rows[0]));
while($i2<$i2max)
$fields[$i2]=$i2;
$i2++;
if($headerrow==true) $row=$fields;
else $i=0; $row=$rows[0];
else
$row=$rows[$i];
$i2=0; $i2max=count($row);
while($i2 < $i2max)// numeric loop (order really matters here)
//while( list($k, $v) = each($row) )
if($i2 != 0) $csv=$csv.$delim;
$v=$row[$fields[$i2]];
if($mode=='EXCEL') //EXCEL 2quote escapes
$newv = '"'.(str_replace('"', '""', $v)).'"';
else //STANDARD
$newv = '"'.(str_replace($escapes2, $escapes, $v)).'"';
$csv=$csv.$newv;
$i2++;
$csv=$csv."\r\n";
$i++;
return $csv;
function SW_ExplodeCSV($csv, $headerrow=true, $mode='EXCEL', $fmt='2D_FIELDNAME_ARRAY')
// SW_ExplodeCSV - parses CSV into 2D array(MS Excel .CSV supported)
// AUTHOR: tgearin2@gmail.com
// RELEASED: 9/21/13 BETA
//SWMessage("SW_ExplodeCSV() - CALLED HERE -");
$rows=array(); $row=array(); $fields=array();// rows = array of arrays
//escape code = '\'
$escapes=array('\r', '\n', '\t', '\\', '\"'); //two byte escape codes
$escapes2=array("\r", "\n", "\t", "\\", "\""); //actual code
if($mode=='EXCEL')
// escape code = ""
$delim=','; $enclos='"'; $esc_enclos='""'; $rowbr="\r\n";
else //mode=STANDARD
// all fields enclosed
$delim=','; $enclos='"'; $rowbr="\r\n";
$indxf=0; $indxl=0; $encindxf=0; $encindxl=0; $enc=0; $enc1=0; $enc2=0; $brk1=0; $rowindxf=0; $rowindxl=0; $encflg=0;
$rowcnt=0; $colcnt=0; $rowflg=0; $colflg=0; $cell="";
$headerflg=0; $quotedflg=0;
$i=0; $i2=0; $imax=strlen($csv);
while($indxf < $imax)
//find first *possible* cell delimiters
$indxl=strpos($csv, $delim, $indxf); if($indxl===false) $indxl=$imax;
$encindxf=strpos($csv, $enclos, $indxf); if($encindxf===false) $encindxf=$imax; //first open quote
$rowindxl=strpos($csv, $rowbr, $indxf); if($rowindxl===false) $rowindxl=$imax;
if(($encindxf>$indxl)||($encindxf>$rowindxl))
$quoteflg=0; $encindxf=$imax; $encindxl=$imax;
if($rowindxl<$indxl) $indxl=$rowindxl; $rowflg=1;
else
//find cell enclosure area (and real cell delimiter)
$quoteflg=1;
$enc=$encindxf;
while($enc<$indxl) //$enc = next open quote
// loop till unquoted delim. is found
$enc=strpos($csv, $enclos, $enc+1); if($enc===false) $enc=$imax; //close quote
$encindxl=$enc; //last close quote
$indxl=strpos($csv, $delim, $enc+1); if($indxl===false) $indxl=$imax; //last delim.
$enc=strpos($csv, $enclos, $enc+1); if($enc===false) $enc=$imax; //open quote
if(($indxl==$imax)||($enc==$imax)) break;
$rowindxl=strpos($csv, $rowbr, $enc+1); if($rowindxl===false) $rowindxl=$imax;
if($rowindxl<$indxl) $indxl=$rowindxl; $rowflg=1;
if($quoteflg==0)
//no enclosured content - take as is
$colflg=1;
//get cell
// $cell=substr($csv, $indxf, ($indxl-$indxf)-1);
$cell=substr($csv, $indxf, ($indxl-$indxf));
else// if($rowindxl > $encindxf)
// cell enclosed
$colflg=1;
//get cell - decode cell content
$cell=substr($csv, $encindxf+1, ($encindxl-$encindxf)-1);
if($mode=='EXCEL') //remove EXCEL 2quote escapes
$cell=str_replace($esc_enclos, $enclos, $cell);
else //remove STANDARD esc. sceme
$cell=str_replace($escapes, $escapes2, $cell);
if($colflg)
// read cell into array
if( ($fmt=='2D_FIELDNAME_ARRAY') && ($headerflg==1) )
$row[$fields[$colcnt]]=$cell;
else if(($fmt=='2D_NUMBERED_ARRAY')||($headerflg==0))
$row[$colcnt]=$cell; //$rows[$rowcnt][$colcnt] = $cell;
$colcnt++; $colflg=0; $cell="";
$indxf=$indxl+1;//strlen($delim);
if($rowflg)
// read row into big array
if(($headerrow) && ($headerflg==0))
$fields=$row;
$row=array();
$headerflg=1;
else
$rows[$rowcnt]=$row;
$row=array();
$rowcnt++;
$colcnt=0; $rowflg=0; $cell="";
$rowindxf=$rowindxl+2;//strlen($rowbr);
$indxf=$rowindxf;
$i++;
//SWMessage("SW_ExplodeCSV() - colcnt = ".$colcnt." rowcnt = ".$rowcnt." indxf = ".$indxf." indxl = ".$indxl." rowindxf = ".$rowindxf);
//if($i>20) break;
return $rows;
...bob 现在可以回到他的电子表格
【讨论】:
以上是关于如何使用 PHP 解析 CSV 文件 [重复]的主要内容,如果未能解决你的问题,请参考以下文章