如何使用 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 文件 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何在php中上传和解析CSV文件

PHP Laravel:如何在将 xl/csv 导入 mysql 时避免重复数据?

如何在 PHP 中解析 csv 在列中具有多行数据

使用PHP在短时间内解析大型CSV文件

如何在 PHP 中对多维数组进行排序 [重复]

Python:忽略文本文件的注释,该文本文件被解析为字典以写入 CSV [重复]