Click to See Complete Forum and Search --> : CSV Iterator


rkolbe
07-10-2007, 11:06 PM
Hello fellow PHP Builders,

I just wrote up a class to iterate over a CSV file and would like come comments so that I can improve it and make it more useful. Right now it does what I need but I would like to make it better for the PHP community as a whole.

I am only going to post the class here but if you wish to see usage and examples (and downloads) please see my site http://white-box.us.

Here it is in all its glory:


/**
* Class: CSVIterator
* Desc: Read a CSV file
* @author: Raymond J Kolbe
* @copyright: July 10th, 2007
*/
class CSVIterator extends ArrayIterator{
private $csvFile; // the location to our file
private $colseparator; // the column separator
private $csvData; // The array iterator object
/**
* @param $file The csv file we wish to parse
* @param $colseparator The column separator for data. Defaults to "," (comma)
* @return void
*/
public function __construct($file, $colseparator = ","){
if(!self::isValidFile($file)){
throw new Exception("File is not valid!");
}
$this->csvFile = $file;
$this->colseparator = $colseparator;
// Get and pass our array of data to ArrayIterator
parent::__construct(self::genCsvArray());
}
/**
* @return An array of the lines from our csv file.
* @throws Exception
*/
private function genCsvArray(){
$lines = @file($this->csvFile, FILE_SKIP_EMPTY_LINES);
if(!$lines){
throw new Exception("Unable to pull lines into array.");
}
return $lines;
}
/**
* @param $file The file in question
* @return boolean True if the file is a file or false if it is not a valid file.
*/
public static function isValidFile($file){
return (is_file($file));
}
/**
* @return Void
* @desc: Moves our array pointer forward.
*/
public function moveForward(){
parent::next();
}
/**
* @return array The next row in our file with each column as its own key
*/
public function getDataRow(){
return explode($this->colseparator, parent::current());
}
/**
* @return boolean Returns true if there is still data in the array, false if there is nothing
*/
public function hasData(){
return (parent::valid());
}
}

NogDog
07-11-2007, 01:11 AM
I'd recommend using the fgetcsv() function to read the CSV file, rather than using file() and then using explode() to break lines into fields. The CSV file protocol is a bit more complex than just splitting on commas, and fgetcsv() is cognizant of that protocol; namely that if a field contains a comma and/or a double-quote, then the field must be quoted with double-quotes and any double-quotes that are part of the field data must be doubled up:

field 1,"field 2 has ""quotes"", and it has a comma",this is field 3

rkolbe
07-11-2007, 08:49 AM
Nice NogDog. I didn't even know about that function...not that I really searched for one or thought there would be issues using file()

Thank you. :cool:

rkolbe
07-12-2007, 12:26 PM
So I decided (and was originally deciding) to implement Iterator instead of extending ArrayIterator since there are additional methods I don't even need from ArrayIterator.

While thinking about how to write this up, I did a search (and like I said before, I did not do before) and found what I believe to be the best code for performing tasks on a CSV file.

Here is the new code, based on code from mortanon at gmail dot com from php.net (http://www.php.net/manual/en/function.fgetcsv.php#57802).

All I ended up changing was the constructor:


public function __construct($file, $delimiter=','){
if(!is_readable($file)){
throw new CSVException("File <".$file."> could not be read.");
}
$this->filePointer = @fopen($file, "r");
$this->delimiter = $delimiter;
}


And creating a destruct:


function __destruct(){
fclose($this->filePointer);
}