fgetcsv

(PHP 3 >= 3.0.8, PHP 4, PHP 5)

fgetcsv -- 从文件指针中读入一行并解析 CSV 字段

说明

array fgetcsv ( int handle [, int length [, string delimiter [, string enclosure]]] )

handle

一个由 fopen()popen()fsockopen() 产生的有效文件指针。

length (可选)

必须大于 CVS 文件内最长的一行。在 PHP 5 中该参数是可选的。如果忽略(在 PHP 5.0.4 以后的版本中设为 0)该参数的话,那么长度就没有限制,不过可能会影响执行效率。

delimiter (可选)

设置字段分界符(只允许一个字符),默认值为逗号。

enclosure (可选)

设置字段环绕符(只允许一个字符),默认值为双引号。该参数是在 PHP 4.3.0 中添加的。

fgets() 类似,只除了 fgetcsv() 解析读入的行并找出 CSV 格式的字段然后返回一个包含这些字段的数组。

fgetcsv() 出错时返回 FALSE,包括碰到文件结束时。

注: CSV 文件中的空行将被返回为一个包含有单个 null 字段的数组,不会被当成错误。

例子 1. 读取并显示 CSV 文件的整个内容

<?php
$row
= 1;
$handle = fopen("test.csv","r");
while (
$data = fgetcsv($handle, 1000, ",")) {
    
$num = count($data);
    echo
"<p> $num fields in line $row: <br>\n";
    
$row++;
    for (
$c=0; $c < $num; $c++) {
        echo
$data[$c] . "<br>\n";
    }
}
fclose($handle);
?>

从 PHP 4.3.5 起,fgetcsv() 的操作是二进制安全的。

注: 该函数对区域设置是敏感的。比如说 LANG 设为 en_US.UTF-8 的话,单字节编码的文件就会出现读取错误。

注: 如果碰到 PHP 在读取文件时不能识别 Macintosh 文件的行结束符,可以激活 auto_detect_line_endings 运行时配置选项。

参见 explode()file()pack()fputcsv()


add a note add a note User Contributed Notes
php [at] barryhunter co uk
27-Oct-2006 07:05
Re: LEON _AT_ TIM-ONLINE _DOT_ NL
12-Oct-2006 09:47

Very Handy Function, thanks! However I needed to tweak it a bit: (remove the var_dump, store the info in the big array when using columnheadings and forward $enclosure to the fgetcsv)

<?php

function parse_csv_file($file, $columnheadings = false, $delimiter = ',', $enclosure = null) {
  
$row = 1;
  
$rows = array();
  
$handle = fopen($file, 'r');
  
   while ((
$data = fgetcsv($handle, 1000, $delimiter, $enclosure )) !== FALSE) {
       if (
$columnheadings == true && $row == 1) {
          
$headingTexts = $data;
       }
       elseif (
$columnheadings == true) {
           foreach (
$data as $key => $value) {
               unset(
$data[$key]);
              
$data[$headingTexts[$key]] = $value;
           }
          
$rows[] = $data;
       }
       else {
          
$rows[] = $data;
       }
      
$row++;
   }
  
  
fclose($handle);
   return
$rows;
}
?>
LEON _AT_ TIM-ONLINE _DOT_ NL
12-Oct-2006 04:47
I wrote this function for myself. I think it's handy:

function parse_csv_file($file, $columnheadings = false, $delimiter = ',', $enclosure = null) {
   $row = 1;
   $rows = array();
   $handle = fopen($file, 'r');
  
   while (($data = fgetcsv($handle, 1000, $delimiter)) !== FALSE) {
       if ($columnheadings == true && $row == 1) {
           $headingTexts = $data;
       }
       elseif ($columnheadings == true) {
           var_dump($data);
           foreach ($data as $key => $value) {
               unset($data[$key]);
               $data[$headingTexts[$key]] = $value;
           }
       }
       else {
           $rows[] = $data;
       }
       $row++;
   }
  
   fclose($handle);
   return $rows;
}
aruggirello, at, tiscali, d0t it
11-Oct-2006 12:20
Whenever you have to parse CSV data that isn't stored in a file, or should you need to encode your data as CSV, you may use my set of functions, featuring:

- bi-directional conversion, meaning you may read as well as write CSV data, to or from your arrays.
- Support for customizable field separator, newline character (also auto-detects and converts Win/Mac/*nix newlines).
- separators, newlines (auto-converted), properly handled within fields enclosed in quotation marks. Last line may or may not be terminated by a newline.
- checks for possibly corrupt data (whenever last field in last row starts, but is not terminated, with a quotation mark).
- Top speed guaranteed by use of strpos(), substr() etc.; room yet for some optimization (especially with associative arrays).
- Clean (no data stored or retrieved from outside function scope, except for CSVstruct array).

Requires PHP 4.3 or newer.
Enjoy!
Andrea Ruggirello

souce available at:
http://web.tiscali.it/caelisoft/provacsv.txt
spam at cyber-space dot nl
07-Sep-2006 09:23
I needed a fast/robust csv parser in PHP that could handle unix-, windows- and mac-style linebreaks.

i takes a csv-string as input and outputs a multidimensional array with lines and fields.

<?php
function parse_csv_php(&$data,$delim=',',$enclosure='"')
{
 
$enclosed=false;
 
$fldcount=0;
 
$linecount=0;
 
$fldval='';
 for(
$i=0;$i<strlen($data);$i++)
 {
 
$chr=$data{$i};
  switch(
$chr)
  {
   case
$enclosure:
   if(
$enclosed&&$data{$i+1}==$enclosure)
   {
    
$fldval.=$chr;
     ++
$i; //skip next char
  
}
   else
    
$enclosed=!$enclosed;
   break;
   case
$delim:
   if(!
$enclosed)
   {
    
$ret_array[$linecount][$fldcount++]=$fldval;
    
$fldval='';
   }
   else
    
$fldval.=$chr;
   break;
   case
"\\r":
   if(!
$enclosed&&$data{$i+1}=="\\n")
     continue;
   case
"\\n":
   if(!
$enclosed)
   {
    
$ret_array[$linecount++][$fldcount]=$fldval;
    
$fldcount=0;
    
$fldval='';
   }
   else
    
$fldval.=$chr;
   break;
   default:
  
$fldval.=$chr;
  }
 }
 if(
$fldval)
 
$ret_array[$linecount][$fldcount]=$fldval;
 return
$ret_array;
}
?>
Skakunov Alexander <i1t2b3 at gmail dot com>
24-Aug-2006 06:42
If you need to import a huge CSV file into a database, use the bulk insert technique instead of many line-by-line inserts:
- MSSQL: bcp tool and "BULK INSERT" SQL
- MySQL: mysqlimport tool and "LOAD DATA INFILE" SQL

As for MySQL, you can use the ready "Quick CSV import" class at http://a4.users.phpclasses.org/browse/package/2917.html
stinkyj at gmail dot com
02-Aug-2006 10:15
the enclosure param defaulting to " and giving a warning if it's an empty string makes this function nearly worthless. csv files do not always have the fields enclosed, and in those cases it doesn't work.
myrddin at myrddin dot myrddin
19-Jul-2006 12:14
Here is a OOP based importer similar to the one posted earlier. However, this is slightly more flexible in that you can import huge files without running out of memory, you just have to use a limit on the get() method

Sample usage for small files:-
-------------------------------------
$importer = new CsvImporter("small.txt",true);
$data = $importer->get();
print_r($data);

Sample usage for large files:-
-------------------------------------
$importer = new CsvImporter("large.txt",true);
while($data = $importer->get(2000))
{
print_r($data);
}

And heres the class:-
-------------------------------------
class CsvImporter
{
   private $fp;
   private $parse_header;
   private $header;
   private $delimiter;
   private $length;
   //--------------------------------------------------------------------
   function __construct($file_name, $parse_header=false, $delimiter="\t", $length=8000)
   {
       $this->fp = fopen($file_name, "r");
       $this->parse_header = $parse_header;
       $this->delimiter = $delimiter;
       $this->length = $length;
       $this->lines = $lines;

       if ($this->parse_header)
       {
           $this->header = fgetcsv($this->fp, $this->length, $this->delimiter);
       }

   }
   //--------------------------------------------------------------------
   function __destruct()
   {
       if ($this->fp)
       {
           fclose($this->fp);
       }
   }
   //--------------------------------------------------------------------
   function get($max_lines=0)
   {
       //if $max_lines is set to 0, then get all the data

       $data = array();

       if ($max_lines > 0)
           $line_count = 0;
       else
           $line_count = -1; // so loop limit is ignored

       while ($line_count < $max_lines && ($row = fgetcsv($this->fp, $this->length, $this->delimiter)) !== FALSE)
       {
           if ($this->parse_header)
           {
               foreach ($this->header as $i => $heading_i)
               {
                   $row_new[$heading_i] = $row[$i];
               }
               $data[] = $row_new;
           }
           else
           {
               $data[] = $row;
           }

           if ($max_lines > 0)
               $line_count++;
       }
       return $data;
   }
   //--------------------------------------------------------------------

}
Ariel asphp at dsgml dot com
01-Jun-2006 06:52
Should you need it, here is a nice and simple function for escaping csv fields properly.

This version is conditional - it only adds quotes if needed:
<?
function csv_escape($str) {
  
$str = str_replace(array('"', ',', "\n", "\r"), array('""', ',', "\n", "\r"), $str, &$count);
   if(
$count) {
       return
'"' . $str . '"';
   } else {
       return
$str;
   }
}
?>

This version is even simpler, but adds quotes even if not needed.
<?
function csv_escape($str) {
   return =
'"' . str_replace('"','""', $str) . '"';
}
?>
jon at jonhassall dot com
26-May-2006 12:54
I modified the code for my own purposes, to return an array with named keys for each field. I tried various alternatives, and this seems to work well with exported Excel data.

<pre><?php
//Move through a CSV file, and output an associative array for each line
ini_set("auto_detect_line_endings", 1);
$current_row = 1;
$handle = fopen("testdatasource.csv", "r");
while ( (
$data = fgetcsv($handle, 10000, ",") ) !== FALSE )
{
  
$number_of_fields = count($data);
   if (
$current_row == 1)
   {
  
//Header line
      
for ($c=0; $c < $number_of_fields; $c++)
       {
          
$header_array[$c] = $data[$c];
       }
   }
   else
   {
  
//Data line
      
for ($c=0; $c < $number_of_fields; $c++)
       {
          
$data_array[$header_array[$c]] = $data[$c];
       }
      
print_r($data_array);
   }
  
$current_row++;
}
fclose($handle);
//Look at my photos www.jonhassall.com !
?></pre>

Jon Hassall
br0derbund at hotmail
19-May-2006 11:32
Thank you donnoman and ramdac, that's exactly the kind of function I needed!

For my use, assigning metadata to images, I actually want the first dimension array key to be a string pulled from the CSV; the unique filename, instead of a number.

Easy, just insert the second dim's key...
Change: $return[]=$row;
To: $return[$data[0]]=$row;
(You may indeed wish to confirm unique values first!)

Also, if you find the fields getting out of sync, increase the $len byte count for the largest cell of data.
simone.sanfratello at tiscali dot it
17-Mar-2006 10:30
to get an array with data from the MS Excel csv format (separated by ; and with string wich contains ; or " optionally delimited by " )

function getcsvxls($buffer)
{
   $buffer = str_replace('""', '"', $buffer);
   $n = strlen($buffer);
   $i = $line = 0;
   $del = false;
   while($i < $n)
   {
       $part = substr($buffer, $i);

       if(
           (substr($part, 0, 1) == ';' && !$del) ||
           (substr($part, 0, 2) == '";' && $del)
       )
       {
           $i ++;
           if($del)
           {
               $str = substr($str, 1, strlen($str) - 1);
               $i ++;
           }
           $data[$line][] = $str;
           $del = false;
           $str = '';
       } else if(substr($part, 0, 2) == "\r\n")
       {
           $data[$line][] = $str;
           $str = '';
           $del = false;
           $line ++;
           $i += 2;
       } else
       {
           if($part[0] == '"')
               $del = true;
           $str .= $part[0];
           $i ++;
       }
   }
   return $data;
}
daniel at softel dot jp
09-Mar-2006 11:03
Note that fgetcsv() uses the system locale setting to make assumptions about character encoding.
So if you are trying to process a UTF-8 CSV file on an EUC-JP server (for example),
you will need to do something like this before you call fgetcsv():

setlocale(LC_ALL, 'ja_JP.UTF8');

[Also not that setlocale() doesn't *permanently* affect the system locale setting]
02-Mar-2006 10:16
For those of you who need to get CSV data from a single line of quoted CSV (comma delimited) values (that may have commas in the data), use this:

<?

function csv_string_to_array($str){

  
$expr="/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/";

  
$results=preg_split($expr,trim($str));

   return
preg_replace("/^\"(.*)\"$/","$1",$results);

}

$str=<<<EOF
"testing, stuff, here","is testing ok",200,456
EOF;

print_r(csv_string_to_array($str));

?>
abu1980 at yahoo dot com
01-Mar-2006 01:18
I was using the function to import a csv file that had some values with "\" included this confused the import function to ignore the End of line and hence an incorrect number of rows is returned to me.

Suggest you try to replace that \ and { brackets as well since they have the same function of an escape character before u import the function

thanks
28-Feb-2006 07:05
beware of characters of binary value == 0, as they seem to make fgetcsv ignore the remaining part of a line where they appear.

Maybe this is normal under some convention I don't know, but a file exported from Excel had those as values for some cells *sometimes*, thus fgetcsv return variable cell counts for different lines.

i'm using php 4.3
gibson.a.paul at gmail.com
02-Dec-2005 06:14
a simple script to output the contents of a CSV file as a nice table - should be totally dynamic and can use any seperator - it does not have to be a comma (,).

I don't perclaim my code is 100% perfect or correct, but it works.  useful if you back up your cell phone address book and it outputs CSV.

It works on the assumtion that the first line is the header row.

<?
//Define what you want the seperator to be, this could be new line, (\n) a tab (\t) or any other char, for obvious reasons avoid using chars that will be present in the string.  Id suggest a comma, or semicolon.
$sep = ",";

//define file to read
$file = "test.txt";

//read the file into an array
$lines = file($file);

//count the array
$numlines = count($lines);

//explode the first (0) line which will be the header line
$headers = explode($sep, $lines[0]);

//count the number of headers
$numheaders = count($headers);

$i = 0;

//start formatting output
echo "<table border = 1 cellpadding = 2><tr>";

//loop through the headers outputting them into their own <TD> cells
while($i<$numheaders){
      
$headers = str_replace("\"", "", $headers);
       echo
"<td>".$headers[$i]."</td>";
      
$i++;
       }

echo
"</tr>";

$y = 1;

//Output the data, looping through the number of lines of data and also looping through the number of cells in each line, as this is a dynamic number the header length has to be reread.
while($y<$numlines){
      
$x=0;
       echo
"<TR>";
               while(
$x<$numheaders){
              
$fields = explode($sep, $lines[$y]);
              
$fields = str_replace("\"", "", $fields);
               echo
"<TD>&nbsp;".$fields[$x]." </TD>";
              
$x++;
                       }
      
$y++;
       echo
"</TR>";
       }

//close the table.
echo "</table>";
?>
donnoman at donovanbray dot com
23-Nov-2005 11:48
/**
 * Based on an example by ramdac at ramdac dot org
 * Returns a multi-dimensional array from a CSV file optionally using the
 * first row as a header to create the underlying data as associative arrays.
 * @param string $file Filepath including filename
 * @param bool $head Use first row as header.
 * @param string $delim Specify a delimiter other than a comma.
 * @param int $len Line length to be passed to fgetcsv
 * @return array or false on failure to retrieve any rows.
 */
function importcsv($file,$head=false,$delim=",",$len=1000) {
   $return = false;
   $handle = fopen($file, "r");
   if ($head) {
       $header = fgetcsv($handle, $len, $delim);
   }
   while (($data = fgetcsv($handle, $len, $delim)) !== FALSE) {
       if ($head AND isset($header)) {
           foreach ($header as $key=>$heading) {
               $row[$heading]=(isset($data[$key])) ? $data[$key] : '';
           }
           $return[]=$row;
       } else {
           $return[]=$data;
       }
   }
   fclose($handle);
   return $return;
}
junk at vhd dot com dot au
14-Nov-2005 08:46
Based on my observations a few comments below I have written a class that incorporates all these features. Here is the class description:

----------

This class will parse a csv file in either standard or MS Excel format.
Two methods are provided to either process a line at a time or return the whole csv file as an array.

It can deal with:
- Line breaks within quoted fields
- Character seperator (usually a comma or semicolon) in quoted fields
- Can leave or remove leading and trailing spaces or tabs
- Can leave or skip empty rows.
- Windows and Unix line breaks dealt with automatically. (Care must be taken with Macintosh format.)

Also, the escape character is automatically removed.

-----------

So basically it should "just work". If it doesn't please send me an email ;-)

You can download it from: http://www.phpclasses.org/browse/package/2672.html

An example on how to use the class and a test.csv file are also provided.
tokai at binaryriot dot com
08-Nov-2005 06:18
Newer PHP versions handle cvs files slightly different than older versions.

"Max Mustermann"|"Muster Road 34b"|"Berlin"    |"Germany"
"Sophie Master" |"Riverstreet"    |"Washington"|"USA"

The extra spaces behind a few fields in the example (which are useful, when you manually manage a small csv database to align the columns) were ignored by fgetcsv from PHP 4.3. With the new 4.4.1 release they get appended to the string, so you end up with "Riverstreet    " instead the expected "Riverstreet".

Easy workaround is to just trim all fields after reading them in.

while ( $data = fgetcsv($database, 32768, "|") )
{
   $i = 0;
  
   while(isset($data[$i]))
   {
       $data[$i] = rtrim($data[$i]);
       $i++;
   }

   ....
}
junk at vhd dot com dot au
25-Oct-2005 12:52
The fgetcsv function seems to follow the MS excel conventions, which means:

- The quoting character is escaped by itself and not the back slash.
(i.e.Let's use the double quote (") as the quoting character:
 
   Two double quotes  "" will give a single " once parsed, if they are inside a quoted field (otherwise neither of them will be removed).

   \" will give \" whether it is in a quoted field or not (same for \\) , and

   if a single double quote is inside a quoted field it will be removed. If it is not inside a quoted field it will stay).

- leading and trailing spaces (\s or \t) are never removed, regardless of whether they are in quoted fields or not.

- Line breaks within fields are dealt with correctly if they are in quoted fields. (So previous comments stating the opposite are wrong, unless they are using a different PHP version.... I am using 4.4.0.)

So fgetcsv if actually very complete and can deal with every possible situation. (It does need help for macintosh line breaks though, as mentioned in the help files.)

I wish I knew all this from the start. From my own benchmarks fgetcsv strikes a very good compromise between memory consumption and speed.

-------------------------
Note: If back slashes are used to escape quotes they can easily be removed afterwards. Same for leading and trailing spaces.
mortanon at gmail dot com
14-Oct-2005 11:05
Hier is an example for a CSV Iterator.

<?php
class CsvIterator implements Iterator
{
   const
ROW_SIZE = 4096;
  
/**
     * The pointer to the cvs file.
     * @var resource
     * @access private
     */
  
private $filePointer = null;
  
/**
     * The current element, which will
     * be returned on each iteration.
     * @var array
     * @access private
     */
  
private $currentElement = null;
  
/**
     * The row counter.
     * @var int
     * @access private
     */
  
private $rowCounter = null;
  
/**
     * The delimiter for the csv file.
     * @var str
     * @access private
     */
  
private $delimiter = null;

  
/**
     * This is the constructor.It try to open the csv file.The method throws an exception
     * on failure.
     *
     * @access public
     * @param str $file The csv file.
     * @param str $delimiter The delimiter.
     *
     * @throws Exception
     */
  
public function __construct($file, $delimiter=',')
   {
      
try {
          
$this->filePointer = fopen($file, 'r');
          
$this->delimiter = $delimiter;
       }
      
catch (Exception $e) {
          
throw new Exception('The file "'.$file.'" cannot be read.');
       }
   }

  
/**
     * This method resets the file pointer.
     *
     * @access public
     */
  
public function rewind() {
      
$this->rowCounter = 0;
      
rewind($this->filePointer);
   }

  
/**
     * This method returns the current csv row as a 2 dimensional array
     *
     * @access public
     * @return array The current csv row as a 2 dimensional array
     */
  
public function current() {
      
$this->currentElement = fgetcsv($this->filePointer, self::ROW_SIZE, $this->delimiter);
      
$this->rowCounter++;
       return
$this->currentElement;
   }

  
/**
     * This method returns the current row number.
     *
     * @access public
     * @return int The current row number
     */
  
public function key() {
       return
$this->rowCounter;
   }

  
/**
     * This method checks if the end of file is reached.
     *
     * @access public
     * @return boolean Returns true on EOF reached, false otherwise.
     */
  
public function next() {
       return !
feof($this->filePointer);
   }

  
/**
     * This method checks if the next row is a valid row.
     *
     * @access public
     * @return boolean If the next row is a valid row.
     */
  
public function valid() {
       if (!
$this->next()) {
          
fclose($this->filePointer);
           return
false;
       }
       return
true;
   }
}
?>

Usage :

<?php
$csvIterator
= new CsvIterator('/path/to/csvfile.csv');
foreach (
$csvIterator as $row => $data) {
  
// do somthing with $data
}
?>
Vladimir Gruzdev
20-Sep-2005 11:09
"cristian DOT zuddas AT gmail DOT com" has posted the corrected function "CSV2Array", but all the same there's an error. For example, function incorrectly processes a simple csv-file from "MS Excel" (http://wsCat.ka.pp.ru/test.csv) in which fields any way contain quotes and CR/LF . I has written working  function which has successfully applied to import of the greater production catalog.

<?php
// --------- Test ---------------------------------------
$str=file_get_contents('http://wsCat.ka.pp.ru/test.csv');
$rows=CSV2Array($str);
echo
'<table border="1" cellSpacing="2" cellPadding="2">';
for(
$i=0;$i<count($rows);$i++)
   {
   echo
'<tr>';
   for (
$j=0;$j<count($rows[$i]);$j++)
       {
         echo
'<td>'.$rows[$i][$j]. '</td>';
       }
   echo
'</tr>';
   }
echo
'</table>';
// -----------------------------------------------------
/**
 *function CSV2Array
 *Convert CSV-text to 2d-array
 *(delimeter = ';', line ending = '\r\n' - only for windows platform)
 * @param string $query (query to database)
 * @return array
 */
function CSV2Array($content)
   {
     if (
$content{strlen($content)-1}!="\r" && $content{strlen($content)-1}!="\n")
        
$content .= "\r\n";

    
$arr=array();
    
$temp=$content;
    
$tma=array();
     while (
strlen($temp)>0)
           {
           if (
$temp{0}=='"')
               {
              
$temp=substr($temp,1);
              
$str='';
               while (
1)
                   {
                  
$matches=array();
                   if (!
preg_match('/^(.*?)"("*?)(;|\r\n)(.*)$/is',$temp,$matches))
                     return
$arr;

                  
$temp=$matches[4];
                   if (
fmod(strlen($matches[2]),2)>0)
                       {
                      
$str.=$matches[1].$matches[2].'"'.$matches[3];
                       continue;
                       }
                     else
                       {
                      
$tma[]=preg_replace('/""/','"',$str.$matches[1].$matches[2]);
                       if (
$matches[3]!=';')
                           {
                          
$arr[]=$tma;
                          
$tma=array();
                           }
                       break;
                       }
                   }
               }
             else
               {
              
$matches=array();
               if (!
preg_match('/^([^;\r\n]*)(;|\r\n)(.*)$/is',$temp,$matches))
                   return
$arr;
              
$tma[]=$matches[1];
              
$temp=$matches[3];
               if (
$matches[2]!=';')
                   {
                  
$arr[]=$tma;
                  
$tma=array();
                   }
               }
           }
     return
$arr;
   }
?>
at lapstore doot de the webmaster
26-Aug-2005 10:16
Some of you might need this:
CSV-Parser optimized for -speed- which can handle linebreaks in quoted strings
This parser can not handle unquoted data containing quotes ("), i.e. apple, ban"ana, cherry

<?php
// csv.inc.php
//
// Usage:
// use argument chopnl=true to autoconvert linebreaks in strings to spaces

$csv_data = "apple; banana; \"multi-\nline\";\"string with \"\" quoted quotes\";\n";
$csv_data .= "second;line;with;trailing;colon\n";
$csv_data .= "third;line;without;trailing;colon;";
$prepared = csv_prepare($csv_data);
while (
$myrow = csv_getrow(&$prepared, false)) {
  echo
"Row ".csv_rownumber(&$prepared).": ";
  for (
$i = 0;$i < $myrow["c"]; $i++) echo "[".htmlspecialchars($myrow[$i])."]";
  echo
"<BR>\n";
}

// ******** public functions ********

function csv_prepare($data) {
  
$rc = array();
  
$rc[0] = strtr($data,array("\r\n" => "\n"));
  
$rc[0] = strtr($rc[0],array("\r" => "\n"));
  
$rc[1] = 0;
  
$rc[2] = strlen($rc[0]);
  
$rc[3] = 0;
   return
$rc;
}

// data[0] = csv-data
// data[1] = startindex
// data[2] = total length csv
// data[3] = currentrow starting from 1 (only for informational purposes)

function csv_rownumber(&$data) {
   return
$data[3];
}
function
csv_getrow(&$data, $chopnl) {
  
$data[3] += 1;
  
$rc = array();   
  
$line = csv__getline(&$data);
  
$line = trim($line);
  
$len = strlen($line);
  
$start = 0;
   if (
$len == 0 && $data[1] >= $data[2]) {
       return
false;
   }
  
  
$iter = 0;
   while (
$iter < 500) {
      
$item = csv__getitem(&$line, $chopnl, &$start, $len);
      
$rc[$iter] = $item;       
      
$iter += 1;
       if (
$start >= $len) break;
   }
  
$rc["c"] = $iter;
   return
$rc;
}

// ************ Internals *********
function csv__getline(&$data) {
  
$sep = csv__extract(&$data[0], "\n", $data[1], $data[2]);
  
$oi = $data[1];
  
$data[1] = $sep + 1;
   return
substr($data[0],$oi,$sep - $oi);
}

function
csv__getitem(&$data, $chopnl, &$start, $len) {
  
$sep = csv__extract(&$data, ";",$start, $len);
  
$rc = trim(substr($data,$start,$sep - $start));
  
$start = $sep + 1;
   if (
substr($rc,0,1) == "\"") {
       if (
substr($rc,strlen($rc) - 1,1) == "\"") {
          
$rc = substr($rc,1,strlen($rc) - 2);
       }
   }
  
$rc = strtr($rc,array("\"\"" => "\""));
   if (
$chopnl) {
      
$rc = strtr($rc,array("\n" => " "));
   }
   return
trim($rc);
}

function
csv__extract(&$data, $sep, $startidx, $len) {
  
$instr = false;
   for (
$i=$startidx;$i < $len; $i+=1) {
      
$c = $data[$i];
       if (
$c == "\"") {
           if (
$instr) {
              
$c2 = ($i < $len - 1) ? $data[$i + 1] : "";
               if (
$c2 == "\"") {
                  
$i += 1;
                   continue;
               } else {
                  
$instr = false;
               }
           } else {
              
$instr = true;
           }
       } elseif (
$c == $sep) {
           if (
$instr) {
               continue;
           } else {
               return
$i;
           }
       }
   }
   return
$len;
}

?>
andychr17 at hotmail dot com
06-Aug-2005 03:30
The man below suggested this to avoid and endless loop from using "!== FALSE":

while (($data = fgetcsv($handle, 1000, ",")) != FALSE) {

This is redundant, if you want to do it with the != you may as well do:

while ($data = fgetcsv($handle, 1000, ",")) {
essaym.net
29-Jun-2005 03:17
Heres a function i wrote because all the other functions on here didnt work quiet as easy as I wanted them to:
It opens the CSV file, and reads it into a 3d array.  If you set $columnsOnly, you will only get the first line.

<?php
function CSV2Array($openFile, $columnsOnly = false)
{
  
$handle = fopen ($openFile,"r");

  
$rows = 0;
   while (!
feof($handle)) {
      
$columns[] = explode(",", fgets($handle, 4096));
       if (
$rows++ == 0 && $columnsOnly)
           break;
   }
  
fclose ($handle);
   return
$columns;
}
?>
cristian DOT zuddas AT gmail DOT com
17-Jun-2005 09:58
"marc at henklein dot com" posted a cool function "CSV2Array", but there's an error. If the last char of the CSV file isn't a carriage return, the function can't take the last field of the last line in the CSV.

Fixed adding this check:

<?
if ($content[strlen($content)-1]!="\r" && $content[strlen($content)-1]!="\n")
      
$content .= "\r\n";
?>

The updated function:

<?
function CSV2Array($content, $delim = ';', $encl = '"', $optional = 1) {
   if (
$content[strlen($content)-1]!="\r" && $content[strlen($content)-1]!="\n")
      
$content .= "\r\n";
  
  
$reg = '/(('.$encl.')'.($optional?'?(?(2)':'(').
          
'[^'.$encl.']*'.$encl.'|[^'.$delim.'\r\n]*))('.$delim.
          
'|\r\n)/smi';
  
  
preg_match_all($reg, $content, $treffer);
  
$linecount = 0;
  
   for (
$i = 0; $i<=count($treffer[3]);$i++) {
      
$liste[$linecount][] = $treffer[1][$i];
       if (
$treffer[3][$i] != $delim)
          
$linecount++;
   }
   unset(
$linecount);
   unset(
$i);
   unset(
$reg);
   unset(
$content);
   unset(
$delim);
   unset(
$encl);
   unset(
$optional);
   unset(
$treffer);
  
   return
$liste;
}
?>
Bart
17-Jun-2005 07:08
The file function reads the file in an array with the EOL still attached, so the +1 is not necessary.
Bob Kolk
18-May-2005 12:50
Dave Meiners above example is great, except that you need to need to add one to the final length to account for EOL.  So it'd be:

while ($data = fgetcsv($handle, ($length+1), ","))
marc at henklein dot com
21-Feb-2005 08:57
for excel-csv-sheets like:

test1;test2;test3;test4;"test5
test5b
test5c";test6

(optionally enclosed by " including line-breaks)

.. i wrote a little function to solve this problem.

<?php

function CSV2Array($content, $delim = ';', $encl = '"', $optional = 1)
{
  
$reg = '/(('.$encl.')'.($optional?'?(?(2)':'(').
'[^'.$encl.']*'.$encl.'|[^'.
$delim.'\r\n]*))('.$delim.'|\r\n)/smi';

  
preg_match_all($reg, $content, $treffer);
  
$linecount = 0;

   for (
$i = 0; $i<=count($treffer[3]);$i++)
   {
      
$liste[$linecount][] = $treffer[1][$i];
       if (
$treffer[3][$i] != $delim)
          
$linecount++;
   }
   return
$liste;
}

// usage for example:
$content = join('',file('test.csv'));
$liste = CSV2Array($content);
print_r($liste);
?>
slyi
20-Feb-2005 02:05
heres a version that take a bugzilla url and spits the results as rss

<?php
$bugzillaurl
="http://www.reactos.com/bugzilla/";
$bugquery= $bugzillaurl . "buglist.cgi?query_format=&bug_severity=blocker&ctype=csv";
header('Content-type: text/xml');
echo
"<?xml version=\"1.0\" ?>\n";
?>

<rss version="2.0">
  <channel>
   <title>bugzilla csv2rss</title>
   <link><?=$bugzillaurl?></link>
   <description>bugzilla csv2rss</description>
   <language>en-us</language>

<?php

$row
= 0;
$handle = fopen($bugquery, "r");
while ((
$data = fgetcsv($handle, 1000, ",")) !== FALSE) {
   if(
$row == '0') { $row = '8';} //skip title stuff
  
else{ // display all bugs
  
$row++;
   echo
"<item>\n";
   echo
"<title>" . htmlspecialchars($data[7], ENT_QUOTES) . "</title>\n";
   echo
"<description>" . htmlspecialchars($data[7], ENT_QUOTES) . "</description>\n";
   echo
"<link>" . $bugzillaurl . "show_bug.cgi?id=" . $data[0] . "</link>\n";
   echo
"</item>\n";
   }
}
fclose($handle);
?>

 </channel>
</rss>
ramdac at ramdac dot org
24-Jan-2005 01:40
Be wary of Example #1 above.

If the file doesn't exist, the application will spit out data to STDOUT and could fill up /tmp space if you're not careful. The output might will look like this:

PHP Warning:  fgetcsv(): supplied argument is not a valid stream resource in...(your filename here)

To get around this, do the following:

<?
row
= 1;
if(!
$handle = fopen("test.csv", "r"))
{
     print
'could not open file. quitting';
     die;
}
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);
?>
Daffy
22-Nov-2004 10:25
This is a little sniplet for all users who has problems with Excel csv files. This functions does almost the same like the linux shell program dos2unix.

/* ------------------8<------------------------*/
function dos2unix ($filename) {
       //open original file
       $fp = fopen($filename,'r');
       //open original file
       $fptmp = fopen($filename.'_tmp','w');
       while(!feof($fp)){
               $line = chop(fgets($fp,4096));
               $ret = ereg_replace(chr(13) . chr(10),"\n",$line);
               $ret = ereg_replace(chr(13),"\n",$ret);
               fwrite($fptmp,$ret);
       }

       fclose($fp);
       fclose($fptmp);
       //remove original file
       unlink($filename);
       //move converted file to old filename
       copy($filename.'_tmp', $filename);
       //remove temp file
       unlink($filename.'_tmp');
}
/* ------------------8<------------------------*/
18-Nov-2004 02:47
example 1 above goes into an infinte loop if the file name is bad because fgetcsv return a false result not identical to false but equl to false

to fix this use != instead of !== in the test as shown below it will still work correctly for the case when the file exists.

while (($data = fgetcsv($handle, 1000, ",")) != FALSE) {
mickoz[at]parodius[dot]com
31-Oct-2004 06:54
In "php at dogpoop dot cjb dot net" post, I would change in his function:

function csv_split($line,$delim=',',$removeQuotes=true)

this code:

if ($line[$i+1] == '"') {

to:

if ($i+1 < strlen($line) && $line[$i+1] == '"') {

The reason is that if the quote (") is at the last line of the string, then it will try to reach an undefined space in the string, therefore giving a PHP error.

Of course his function is great if your line is a true compatible csv line, but I leave that to you to judge if you can do this assumption ;-)
aidan at php dot net
01-Jul-2004 09:47
If you're looking to parse CSV files but find fgetcvs insufficient, check out http://pear.php.net/package/File
dave146 at burtonsys dot com
18-Jun-2004 04:29
To convert .dbf files (dBase/xBase/FoxPro/Clipper/etc.) files
to .csv, so that you can read them with f