array('string1', 'string2', 'string3', 'the "string4"') static public function parseString($string, $separator = ',') { $values = array(); $string = str_replace("\r\n", '', $string); // eat the traling new line, if any if ($string == '') return $values; $tokens = explode($separator, $string); $count = count($tokens); for ($i = 0; $i < $count; $i++) { $token = $tokens[$i]; $len = strlen($token); $newValue = ''; if ($len > 0 and $token[0] == '"') { // if quoted $token = substr($token, 1); // remove leading quote do { // concatenate with next token while incomplete $complete = Csv::_hasEndQuote($token); $token = str_replace('""', '"', $token); // unescape escaped quotes $len = strlen($token); if ($complete) { // if complete $newValue .= substr($token, 0, -1); // remove trailing quote } else { // incomplete, get one more token $newValue .= $token; $newValue .= $separator; if ($i == $count - 1) throw new Exception('Illegal unescaped quote.'); $token = $tokens[++$i]; } } while (!$complete); } else { // unescaped, use token as is $newValue .= $token; } $values[] = $newValue; } return $values; } static public function escapeString($string) { $string = str_replace('"', '""', $string); if (strpos($string, '"') !== false or strpos($string, ',') !== false or strpos($string, "\r") !== false or strpos($string, "\n") !== false) { $string = '"'.$string.'"'; } return $string; } // checks if a string ends with an unescaped quote // 'string"' => true // 'string""' => false // 'string"""' => true static public function _hasEndQuote($token) { $len = strlen($token); if ($len == 0) return false; elseif ($len == 1 and $token == '"') return true; elseif ($len > 1) { while ($len > 1 and $token[$len-1] == '"' and $token[$len-2] == '"') { // there is an escaped quote at the end $len -= 2; // strip the escaped quote at the end } if ($len == 0) return false; // the string was only some escaped quotes elseif ($token[$len-1] == '"') return true; // the last quote was not escaped else return false; // was not ending with an unescaped quote } } // very basic separator detection function static public function detectSeparator($filename, $separators = array(',', ';')) { $file = fopen($filename, 'r'); $string = fgets($file); fclose($file); $matched = array(); foreach ($separators as $separator) if (preg_match("/$separator/", $string)) $matched[] = $separator; if (count($matched) == 1) return $matched[0]; else return null; } } class CsvReader implements Iterator { protected $fileHandle = null; protected $position = null; protected $filename = null; protected $currentLine = null; protected $currentArray = null; protected $separator = ','; public function __construct($filename, $separator = ',') { $this->separator = $separator; $this->fileHandle = fopen($filename, 'r'); if (!$this->fileHandle) return; $this->filename = $filename; $this->position = 0; $this->_readLine(); } public function __destruct() { $this->close(); } // You should not have to call it unless you need to // explicitly free the file descriptor public function close() { if ($this->fileHandle) { fclose($this->fileHandle); $this->fileHandle = null; } } public function rewind() { if ($this->fileHandle) { $this->position = 0; rewind($this->fileHandle); } $this->_readLine(); } public function current() { return $this->currentArray; } public function key() { return $this->position; } public function next() { $this->position++; $this->_readLine(); } public function valid() { return $this->currentArray !== null; } protected function _readLine() { if (!feof($this->fileHandle)) $this->currentLine = trim(utf8_encode(fgets($this->fileHandle))); else $this->currentLine = null; if ($this->currentLine != '') $this->currentArray = Csv::parseString($this->currentLine, $this->separator); else $this->currentArray = null; } } class CsvWriter { protected $fileHandle = null; public function __construct($filename, $mode = 'w') { if ($mode != 'w' and $mode != 'a') throw new Exception('CsvWriter only accepts "w" and "a" mode.'); $this->fileHandle = fopen($filename, $mode); if (!$this->fileHandle) throw new Exception("Impossible to open file $filename."); } public function __destruct() { $this->close(); } public function addLine(array $values) { foreach ($values as $key => $value) { $values[$key] = utf8_decode(Csv::escapeString($value)); } $string = implode(',', $values) . "\r\n"; fwrite($this->fileHandle, $string); } // You should not have to call it unless you need to flush the // data from the buffer to your file explicitly before the // end of your script public function close() { if ($this->fileHandle) { fclose($this->fileHandle); $this->fileHandle = null; } } }